Meaning of ** (and other symbols) in a Symfony Glob Pattern - php

A few years back Symfony introduced glob patterns for importing and loading configuration files. You can see an example of this in the stock kernel class
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$confDir = $this->getProjectDir() . '/config';
$routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob');
}
Those glob patterns expand to look like this
$routes->import("/path/to/symfony/config/{routes}/*.{php,xml,yaml,yml}", ...
$routes->import("/path/to/symfony/config/{routes}/dev/**/*.{php,xml,yaml,yml}", ...
$routes->import("/path/to/symfony/config/{routes}.{php,xml,yaml,yml}", ...
This brings a few questions to mind. First off -- what is the ** pattern? That's not a standard part of PHP's glob syntax. I presume by context it means "everything in this folder and all sub-folders", but I can't find official docs on Symfony's glob syntax to know for sure.
Other things about the patterns are odd -- like {routes}. Normally you put things in braces when you want to match a list -- but this uses braces with only one item in the list which makes me think I don't understand what's going on?
When I go spelunking in the source code the methods involved are big ones and not the most intuitive to follow.
Is Symfony's glob syntax an implementation of a standard glob syntax? Or is it its own thing, and if so, what extra features does it have beyond the standard wildcards? (regexes? more special things like **? does a single word in a brace mean anything?)

Related

Remove method by name from php class (not in runtime)

Situation
I have a file which contains 2-3 classes. Each class may and may not contain a destructor function __destruct().
I am retrieving these classes using composer and have no real control over it, neither I can selectively say 'go on and ignore lines X~Z' where the destructor calls are located, as these lines may slightly change.
I need to get rid of all the destructors - or at least prevent them from executing on object destruction, as other part of code which I have no control over jumps into endless destruct loop.
Approaches I tried and considered:
Regex
My initial thought was to write a regex, that would match all these functions and removed them (best effort regex, I am sure there would be corner cases for which the regex would not work), but eventually, after remembering various questions about using regexes inappropriately, I reconsidered - not mentioning the fact, that coming up with working regex seems to be a problem for me as well.
Curly brackets block used for nested scope in C
I have some background in C, so for a second I thought I could just match all the lines with function __destruct(), verify there is no { on the same line and comment the definition out, so I would get something like this:
# function __destruct()
{
echo "initiating destructor function";
$this->callToSomething();
}
Which I thought would not execute (or at very least - would not execute on object destruction, which is why I need to comment this out). But PHP does not allow this syntax and I can not do that. Maybe it allows for some other syntax which would achieve the same results?
Parsing, Reflection? ...?
I do not really want to write myself a PHP parser of its own for this task, but if that is what it will take, I will do it. But since there is already a Reflection class in PHP, maybe there would be a way to load class into the memory, remove the destructors in runtime and then overwrite class definition on disk?
I do not see why you cannot use a regex here. Sure, you need a more advanced pattern.
But you should be never forced to touch it.
Here is a pattern that finds __destruct() blocks:
function\s+__destruct\s*\(\s*\)\s*({(?>[^{}\\]++|\\.|(?1))*+})
Demo
All you need to do is to load the files in question, run
$re = '/function\s+__destruct\s*\(\s*\)\s*({(?>[^{}\\\\]++|\\\\.|(?1))*+})/s';
$result = preg_replace($re, '', $str);
And save the files back to disk. C'est ça.

How can i fix my eval()'d code line: 1 issue

I keep getting
file: C:\xampp\htdocs\doit.php(45) : eval()'d code line: 1
I have searched the site and can not find a fix that works for me this is the code that I am using that is giving the issue
$ec = "\$sucrate=" . str_replace(array("LEVEL", "EXP", "WILL", "IQ"), array($player['level'], $player['exp'], $player['will'], $player['IQ']), $r['crimePERCFORM']) . ";";
eval($ec);
The string you are building would need quotes around the str_replace'd string (and possibly another string_replace pair also to prevent quote issues).
Example:
$ec = "\$sucrate='" . str_replace(array("LEVEL", "EXP", "WILL", "IQ"), array($player['level'], $player['exp'], $player['will'], $player['IQ']), $r['crimePERCFORM']) . "';";
However, while that should fix your issue, there is almost never a good case for using eval. It will certainly leave your code vulnerable to some sort of remote execution hack no matter what "protections" you put in place that would allow anyone to run any code on your server as if it was written by you.
This would do exactly that same thing, which is just setting the $sucrate variable with your replaced values.
$sucrate = str_replace(array("LEVEL", "EXP", "WILL", "IQ"), array($player['level'], $player['exp'], $player['will'], $player['IQ']), $r['crimePERCFORM']);
I got carried away and have thought about how to improve it this might not actually be an answer to your question but more of a food-for-thought. I see two solutions, and both are kind of invasive.
Of course both solutions only work in all the cases if your formula only use "variables" like in your example with ((WILL*0.8)/2.5)+(LEVEL/4). If you have more complex formulas you'd have do adapt my solutions.
Wrapping eval and don't inject all the inputs in the eval'd code
Assuming the formulas are under your control and not user-supplied you could improve your eval by not injecting all the inputs in your eval'd code but only the formula. This way you don't have to escape the inputs, you only have to make sure the formula is syntactically correct.
function calculateFormula($_vars, $_values, $_formula) {
// This transforms your formula into PHP code which looks
// like this: (($WILL*0.8)/2.5)+($LEVEL/4)
$_cleanFormula = str_replace(
$_vars,
array_map(function($v) { return '$' . $v; }, $_vars),
$_formula
);
// create the $WILL, $LEVEL, $IQ and $EXP variables in the local scope
extract(array_combine($_vars, $_values));
// execute the PHP-formula
return eval('return ' . $_cleanFormula . ';');
}
// Use it like this, instead of eval
$sucrate = calculateFormula(
array("LEVEL", "EXP", "WILL", "IQ"),
array($player['level'], $player['exp'], $player['will'], $player['IQ']),
$r['crimePERCFORM']);
This still uses eval, so security-wise this would be the worst solution. But this would be the closest to what you have now.
Using Symfony's Expression Language
The more secure option would be to use something like the symfony's expression language component. Now you don't need the whole symfony framework in your application, the expression language component can be used on it's own. This could be a big change, this depends on how your existing codebase looks. If you have not used composer or namespaces in your project, this change might be too big.
require_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
$expressionLanguage = new ExpressionLanguage();
$sucrate = $expressionLanguage->evaluate(
$r['crimePERCFORM'],
array(
"LEVEL" => $player['level'],
"EXP" => $player['exp'],
"WILL" => $player['will'],
"IQ" => $player['IQ'],
)
);
As I've said this is maybe a huge change and you might have to get acquainted with composer if you don't know it already.

Php-parser printer with max line length functionality

I wonder if it exists pretty printer for php-parser which is capable to enforce desired maximum line length?
(It seems rather simple to implement for some basic cases (array elements list, function arguments list), but it starts to be puzzling with variable expressions etc.)
As far as I know there's no existing pretty printer for PHP-Parser that takes a right margin into account.
There's the standard pretty printer of PHP-Parser itself.
There's also a PSR-2 pretty printer made for PHP-Parser.
DIY
If these don't suffice, you'll have to write a pretty printer yourself.
IMHO this shouldn't be to hard. You can simply wrap when a node exceeds the right margin and indent 4 spaces (or whatever you use). Then you can start optimizing things like array definitions and such.
Sorry for the late reply. You could use PHP Front too. Indentation is done for all nestings of statements, 2 spaces per nesting.
Some customized Indentation is possible. And it is available in PHP Front.
The parser and the pretty printer are also tested together using the test-files of the source-distribution of PHP.
Each test-file is parsed, pretty-printed, parsed and pretty-printed again.
The correctness of this round-trip is tested by performing a diff between the two parsed and the two pretty-printed files.
However I got recommendation to use Standard one as it has many features. It has variable expressions and array expressions feautures. Where as in PHP front, it is still some bugs available to use arrays.
Standard Pretty Printer: (Variable Expressions & Array)
public function pExpr_Variable(Expr\Variable $node) {
if ($node->name instanceof Expr) {
return '${' . $this->p($node->name) . '}';
} else {
return '$' . $node->name;
}
}
public function pExpr_Array(Expr\Array_ $node) {
return 'array(' . $this->pCommaSeparated($node->items) . ')';
}

What is the most efficient way of getting the namespace of a class in PHP 5.5?

PHP 5.5 introduced a neat class name resolution method using the ::class syntax. I imagine this made life easier for a lot of people. It certainly did for me.
But now I find myself having to go one step further: getting the namespace of a certain class. This isn't a hard programming task. It can be achieved with string operations or arrays:
$parts = explode('\\', \Foo\Bar\Baz::class);
array_pop($parts);
return implode('\\', $parts); // returns "Foo\Bar"
But this is an overkill, since I'm using this a lot (as a means to metaprogramming). It actually shows up as a bottleneck when I profile the application. Anyone got an idea for a more efficient solution?
At least
$class = \Foo\Bar\Baz::class;
$namespace = substr($class, 0, strrpos($class, '\\'));
should be more efficient.

Why don't PHP attributes allow functions?

I'm pretty new to PHP, but I've been programming in similar languages for years. I was flummoxed by the following:
class Foo {
public $path = array(
realpath(".")
);
}
It produced a syntax error: Parse error: syntax error, unexpected '(', expecting ')' in test.php on line 5 which is the realpath call.
But this works fine:
$path = array(
realpath(".")
);
After banging my head against this for a while, I was told you can't call functions in an attribute default; you have to do it in __construct. My question is: why?! Is this a "feature" or sloppy implementation? What's the rationale?
The compiler code suggests that this is by design, though I don't know what the official reasoning behind that is. I'm also not sure how much effort it would take to reliably implement this functionality, but there are definitely some limitations in the way that things are currently done.
Though my knowledge of the PHP compiler isn't extensive, I'm going try and illustrate what I believe goes on so that you can see where there is an issue. Your code sample makes a good candidate for this process, so we'll be using that:
class Foo {
public $path = array(
realpath(".")
);
}
As you're well aware, this causes a syntax error. This is a result of the PHP grammar, which makes the following relevant definition:
class_variable_declaration:
//...
| T_VARIABLE '=' static_scalar //...
;
So, when defining the values of variables such as $path, the expected value must match the definition of a static scalar. Unsurprisingly, this is somewhat of a misnomer given that the definition of a static scalar also includes array types whose values are also static scalars:
static_scalar: /* compile-time evaluated scalars */
//...
| T_ARRAY '(' static_array_pair_list ')' // ...
//...
;
Let's assume for a second that the grammar was different, and the noted line in the class variable delcaration rule looked something more like the following which would match your code sample (despite breaking otherwise valid assignments):
class_variable_declaration:
//...
| T_VARIABLE '=' T_ARRAY '(' array_pair_list ')' // ...
;
After recompiling PHP, the sample script would no longer fail with that syntax error. Instead, it would fail with the compile time error "Invalid binding type". Since the code is now valid based on the grammar, this indicates that there actually is something specific in the design of the compiler that's causing trouble. To figure out what that is, let's revert to the original grammar for a moment and imagine that the code sample had a valid assignment of $path = array( 2 );.
Using the grammar as a guide, it's possible to walk through the actions invoked in the compiler code when parsing this code sample. I've left some less important parts out, but the process looks something like this:
// ...
// Begins the class declaration
zend_do_begin_class_declaration(znode, "Foo", znode);
// Set some modifiers on the current znode...
// ...
// Create the array
array_init(znode);
// Add the value we specified
zend_do_add_static_array_element(znode, NULL, 2);
// Declare the property as a member of the class
zend_do_declare_property('$path', znode);
// End the class declaration
zend_do_end_class_declaration(znode, "Foo");
// ...
zend_do_early_binding();
// ...
zend_do_end_compilation();
While the compiler does a lot in these various methods, it's important to note a few things.
A call to zend_do_begin_class_declaration() results in a call to get_next_op(). This means that it adds a new opcode to the current opcode array.
array_init() and zend_do_add_static_array_element() do not generate new opcodes. Instead, the array is immediately created and added to the current class' properties table. Method declarations work in a similar way, via a special case in zend_do_begin_function_declaration().
zend_do_early_binding() consumes the last opcode on the current opcode array, checking for one of the following types before setting it to a NOP:
ZEND_DECLARE_FUNCTION
ZEND_DECLARE_CLASS
ZEND_DECLARE_INHERITED_CLASS
ZEND_VERIFY_ABSTRACT_CLASS
ZEND_ADD_INTERFACE
Note that in the last case, if the opcode type is not one of the expected types, an error is thrown – The "Invalid binding type" error. From this, we can tell that allowing the non-static values to be assigned somehow causes the last opcode to be something other than expected. So, what happens when we use a non-static array with the modified grammar?
Instead of calling array_init(), the compiler prepares the arguments and calls zend_do_init_array(). This in turn calls get_next_op() and adds a new INIT_ARRAY opcode, producing something like the following:
DECLARE_CLASS 'Foo'
SEND_VAL '.'
DO_FCALL 'realpath'
INIT_ARRAY
Herein lies the root of the problem. By adding these opcodes, zend_do_early_binding() gets an unexpected input and throws an exception. As the process of early binding class and function definitions seems fairly integral to the PHP compilation process, it can't just be ignored (though the DECLARE_CLASS production/consumption is kind of messy). Likewise, it's not practical to try and evaluate these additional opcodes inline (you can't be sure that a given function or class has been resolved yet), so there's no way to avoid generating the opcodes.
A potential solution would be to build a new opcode array that was scoped to the class variable declaration, similar to how method definitions are handled. The problem with doing that is deciding when to evaluate such a run-once sequence. Would it be done when the file containing the class is loaded, when the property is first accessed, or when an object of that type is constructed?
As you've pointed out, other dynamic languages have found a way to handle this scenario, so it's not impossible to make that decision and get it to work. From what I can tell though, doing so in the case of PHP wouldn't be a one-line fix, and the language designers seem to have decided that it wasn't something worth including at this point.
My question is: why?! Is this a "feature" or sloppy implementation?
I'd say it's definitely a feature. A class definition is a code blueprint, and not supposed to execute code at the time of is definition. It would break the object's abstraction and encapsulation.
However, this is only my view. I can't say for sure what idea the developers had when defining this.
You can probably achieve something similar like this:
class Foo
{
public $path = __DIR__;
}
IIRC __DIR__ needs php 5.3+, __FILE__ has been around longer
It's a sloppy parser implementation. I don't have the correct terminology to describe it (I think the term "beta reduction" fits in somehow...), but the PHP language parser is more complex and more complicated than it needs to be, and so all sorts of special-casing is required for different language constructs.
My guess would be that you won't be able to have a correct stack trace if the error does not occur on an executable line... Since there can't be any error with initializing values with constants, there's no problem with that, but function can throw exceptions/errors and need to be called within an executable line, and not a declarative one.

Categories