Comments (7)
I have found a potential issue when using League\Plates\Variable
methods on an object. It's possible that methods can collide. Consider this class:
<?php
class HTML
{
public function css($url)
{
return '<link rel="stylesheet" href="' . $url . '" />';
}
}
You may potentially use this in a template like this:
<html>
<head>
<?=$html->css('all.css')->raw()?>
<head>
</html
However, since the method css()
exists on the League\Plates\Variable
class as well, the $html
method will never be called. The simple answer here is to simply check if the object has a method called "css", and if so call it. Like so:
public function css()
{
if (method_exists($this->variable, 'css')) {
return new self(call_user_func_array([$this->variable, 'css'], func_get_args()));
}
return css_escaping_function($this->variable);
}
While this works great for defined methods (like in our example), it doesn't work for methods made available through an object's magic __call()
method. The function method_exists()
will always return false
. There is really no proper way to determine if the __call()
method accepts or rejects any particular method. The best "hack" I can come up with is to simply determine if a __call()
magic method exists, and try to call the method. If an exception is thrown, it obviously doesn't exist. Something like this:
<?php
public function css()
{
if (method_exists($this->variable, 'css')) {
return new self(call_user_func_array([$this->variable, 'css'], func_get_args()));
} elseif (!method_exists($this->variable, 'css') and is_callable([$this->variable, 'css'])) {
try {
// Try to call the method
$response = call_user_func_array([$this->variable, 'css'], func_get_args());
// Success method call, this must be a valid method
// Return the value is the response isn't null or false
if (!in_array($response, array(null, false))) {
return new self($response);
}
} catch (\Exception $e) {
// An exception was thrown, clearly the method doesn't exist
// Just ignore
}
}
return css_escaping_function($this->variable);
}
Is this an acceptable solution to the problem, or is there a better way?
from plates.
So, the next big challenge I see is preventing double escaping. Unlike the compiled approach where variables are only escaped on output, this method will have variables triggering __toString()
anytime a string operation is performed, and that may not always be ideal.
Double escaping example
No escaping issue:
// $city->name = '<strong>Toronto</strong>'
// Outputs: <STRONG>TORONTO</STRONG>
<?=$this->batch($city->name, 'strtoupper')?>
Double escaping issue due to string concatenation:
// $city->name = '<strong>Toronto</strong>'
// $city->province = 'Ontario'
// Outputs: <STRONG>TORONTO</STRONG> (ONTARIO)
<?=$this->batch($city->name . ' (' . $city->province . ')', 'strtoupper')?>
Fixed double escaping issue by outputting function response with raw()
:
// $city->name = '<strong>Toronto</strong>'
// $city->province = 'Ontario'
// Outputs: <STRONG>TORONTO</STRONG> (ONTARIO)
<?=$this->batch($city->name . ' (' . $city->province . ')', 'strtoupper')->raw()?>
Fixed double escaping issue by adding raw()
prior to string concatenation:
// $city->name = '<strong>Toronto</strong>'
// $city->province = 'Ontario'
// Outputs: <STRONG>TORONTO</STRONG> (ONTARIO)
<?=$this->batch($city->name->raw() . ' (' . $city->province->raw() . ')', 'strtoupper')?>
Things to consider:
- Should extension functions be responsible for escaping? For example, they could automatically return their results as an instance of
League\Plates\Variable
. - Any time two variables are joined (ie.
<?=$user->first_name . ' ' . $user->last_name?>
) they will be escaped using the HTML approach. - Often times it's preferred that escaping happens after template functions are applied. However, passing a template variable to a function will trigger
__toString()
. - It may be possible to have template functions automatically get the
raw()
value of a template variable before manipulating it by detecting if it's variable type the class name
from plates.
- I personally don't like the method_exists() approach for preventing naming collisions. But this project is not the first one using proxies. The common approach is prefixing custom methods with a double underscore (like PHP itself does with magic functions).
- Why does passing a template var to a function trigger __toString() ? That shouldn't be the case unless you cast it to string.
from plates.
I personally don't like the method_exists() approach for preventing naming collisions. But this project is not the first one using proxies. The common approach is prefixing custom methods with a double underscore (like PHP itself does with magic functions).
Right, but technically double underscored functions are reserved by PHP. Also, that still doesn't prevent collisions, they're still possible, so a check is still required. Finally, that would really make the API a lot uglier: <?$name->__css()?>
.
Why does passing a template var to a function trigger __toString() ? That shouldn't be the case unless you cast it to string.
True, the passing of the variable doesn't trigger it. What I meant was that since all template functions modified strings, that __toString()
will be triggered, and it's something that has to be considered, especially around preventing double escaping.
from plates.
Originally seen on #23...
https://gist.github.com/shadowhand/039d72433aa262f10b91
This is (roughly) an implementation of automatic escaping in views. It's incomplete, but should be enough to show what I mean about a read-only mode that triggers automatic escaping.
from plates.
@shadowhand If I understand your example correctly, it doesn't take into account multidimensional arrays or objects.
from plates.
Having been pretty excited about the possibilities here yesterday, today I find myself asking if this approach is worth all the complexity. I don't love the idea of working with proxied variable objects—I'd rather just work directly with the strings, arrays or objects I passed to the template. I'm also nervous about how annoying the double-escaping issue could become. Developers will always have to be thinking "is this going to cause a double/triple escape?". And the object method conflict issue is real, and who knows how many more issues we might stumble upon.
My original goal was to find a very good solution to automatic escaping—one that works for strings, arrays and objects, including any variations nested within these. While this approach works, it really is a pretty big hack of the language for the sole purpose of automatic escaping. Does the end justify the means?
For me, I keep going back to the proposed compiler (#23). To me this is a much more elegant solution to the problem. I think the reason it works so well is because it's actually solving the escaping problem in the right place—at the point of output. I know developers who use native PHP templates hate the idea of compiling, but the performance impact here is seriously negligible. And just because the templates are compiled, doesn't mean they are not PHP templates!
Is it really that smart to manipulate PHP this much just to avoid a compiling stage that actually provides the solution to the problem in a much cleaner way? Is it okay to use PHP to create hundreds or thousands of new objects, when we could just use PHP to "inject" the escape functions right where they're needed? Some people have said compiling introduces too much magic. I can tell you right now, this approach uses a lot more magic than that!
I like PHP templates because their PHP. No new syntax to learn. I know the functions available to me, including those I've developed. My editor has syntax highlighting and auto-completing built in. It's what I know. Personally, I really don't care if the template engine has to so some compiling in the background IF that means it's going to be safer and quicker to develop with.
from plates.
Related Issues (20)
- Replace 'callback' type with 'callable' in PhpDoc HOT 1
- Don't seem to be able to install last version using composer HOT 1
- Website blocked in India HOT 1
- Whats holding 3.5v release? HOT 1
- Any hope of documented Themes functionality being release? If not, recommended workaround? HOT 3
- Data documentation outdated
- I have cloned a php project from git. The project is using platesphp. But i am not able to run my branch in localhost. HOT 1
- league/plates v4.0.0-alpha to v3.4.0 create error HOT 3
- Atualização HOT 1
- Release 3.5 with PHP 8.2 compatibility and theme support? HOT 2
- Variables from the rendering process leak into templates
- Bug when pushing to sections inside of fetched templates HOT 8
- Documentation "Engine -> Accessing the engine and template"
- Does it support custom template tags? HOT 2
- How to properly use asset extension? HOT 2
- I can't use League\Plates\Extension\Asset;
- Expected behaviour for multiple sections build over multiple layouts HOT 1
- Deprecated dynamic property in extensions HOT 6
- Passing data to insert() inside layout() HOT 2
- Project status?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from plates.