$map_class_name = __NAMESPACE__.'\\'.$map_class_name;
$map = new $map_class_name;
Is it possible to do this in one line like this?
$map = new (__NAMESPACE__.'\\'.$map_class_name);
The above gives
syntax error, unexpected '('
The answer is no, you can't use new with string or expression.
But you can use
$map = new $map_class_name;
Since you use class from current namespace(__NAMESPACE__) you don't have to prefix classname, namespace will be implied.
Alternative solutions
You might use Reflection
$map = (new \ReflectionClass(__NAMESPACE__."\\$map_class_name"))->newInstance();
Another way is to not break line, but I guess this misses the point.
$map_class_name = __NAMESPACE__.'\\'.$map_class_name; $map = new $map_class_name;
IIFE is also possible
$map = (function($n){return new $n;})(__NAMESPACE__."\\$map_class_name");
Related
How to extract method name and argument from a string code?
Example :
$obj->MethodA($obj->MethodB(param1,$obj->MethodX()))
I tried using this regex but not work
preg_match_all('/\$obj->(\w+)\(((\w|,| )*)\)/', $string, $matches)
The aim is to extract all the method calls as well as their arguments, so the matches should match
$obj->MethodA($obj->MethodB(param1, $obj->MethodX()))
$obj->MethodB(param1,$obj->MethodX())
$obj->MethodX()
Some people might say using regex is not ideal. Is there any alternative?
I would recommend using something that understands PHP's syntax. For example this library - https://github.com/nikic/PHP-Parser
Regex would probably get unwieldy pretty fast.
Quick example using the mentioned PHP-Parser.
use PhpParser\Node\Expr\CallLike;
use PhpParser\NodeFinder;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
$php = '<?php $obj->MethodA($obj->MethodB("test",$obj->MethodX()));';
$parserFactory = new ParserFactory();
$parser = $parserFactory->create(ParserFactory::PREFER_PHP7);
$statements = $parser->parse($php);
$finder = new NodeFinder();
// Or if you only want method calls, we could also find instances of MethodCall
$calls = $finder->findInstanceOf($statements, CallLike::class);
$printer = new Standard();
foreach ($calls as $call) {
echo $printer->prettyPrintExpr($call) , "\n";
}
// Output
//$obj->MethodA($obj->MethodB("test", $obj->MethodX()))
//$obj->MethodB("test", $obj->MethodX())
//$obj->MethodX()
Before I could create an object in this way in PHP:
use myFolder\models\Document;
$nameClass = 'Document';
$model = new $nameClass;
This used to work without problems.
But now it gives me a error
Is it for the PHP version or configuration?
What is the problem?
Why it sentence works fine?
$model = new Document;
and the previous one fails?
Thanks
The problem was the namespace; it should have been
use myFolder\models\Document;
$nameClass = 'myFolder\models\Document';
$model = new $nameClass;
How can we use variable in classes name on call?
For example i have a class That called like this:
<?php
$ig = new Instagram();
$ig->people->getIdByName("param1",array('a','b','c'),"param2");
So how can i call this method like this?:
<?php
$ig = new Instagram();
$main = 'people';
$sub = 'getIdByName';
$content = '"param1",array('a','b','c'),"param2"';
$ig->$main->$sub($content);
Methods calls would work just like you showed in the question. So this part of code is OK:
$main = 'people';
$sub = 'getIdByName';
$ig->$main->$sub();
What about last method's arguments, there 2 options:
PHP >= 5.6
Variadic parameters were introduced in PHP 5.6, so you can save your arguments as an array, and just pass them with ...:
$args = ["param1",['a','b','c'],"param2"];
$ig->$main->$sub(...$args);
PHP < 5.6
In this case you should use call_user_func_array()
$main = 'people';
$sub = 'getIdByName';
$args = ["param1",['a','b','c'],"param2"];
$main = $ig->$main;
call_user_func_array([$main, $sub], $args];
Although #Andrew is right, this approach is not flexible and must not be used as an architecture for API providers.
With reflection, it's easy to get the start and end line e.g. of a method in the source file: ReflectionFunctionAbstract::getFileName(), ReflectionFunctionAbstract::getStartLine(), ReflectionFunctionAbstract::getEndLine() provide this functionality. However, this doesn't seem to work with properties. What's the best way to extract at least the start line and the file name of a property declaration in a class definition?
It's not trivial but also not too hard.
You can get the class a property is defined in via Reflection. And from there you can get the filename. All you have to do then is either tokenize the file and check at what line the property declaration or simply go over the file line by line and do string matching.
Here is one possible way to do that:
$reflector = new ReflectionProperty('Foo', 'bar');
$declaringClass = $reflector->getDeclaringClass();
$classFile = new SplFileObject($declaringClass->getFileName());
foreach ($classFile as $line => $content) {
if (preg_match(
'/
(private|protected|public|var) # match visibility or var
\s # followed 1 whitespace
\$bar # followed by the var name $bar
/x',
$content)
) {
echo $line + 1;
}
}
And here is a demo to show that it works
Obviously, the above solution assumes the property to be declared in a certain fashion. It also assumes you have one class per file. If you cannot be sure this is the case, tokenization is the better option. But it's also more difficult.
Use roave/better-reflection
$classInfo = (new BetterReflection())
->reflector()
->reflectClass($class);
foreach ( $classInfo->getProperties() as $reflectionProperty) {
$declaringClass = $reflectionProperty->getDeclaringClass()->getFileName();
$declaringSource = $reflectionProperty->getDeclaringClass()->getLocatedSource()->getSource();
$sourceLines = explode("\n", $declaringSource);
$propertySource = join("\n", array_slice($sourceLines, $reflectionProperty->getStartLine(), $reflectionProperty->getEndLine()-$reflectionProperty->getStartLine()));
$properties[$reflectionProperty->getName()] = [
'declaringClass' => $declaringClass,
'source' => $propertySource,
'startLine' => $reflectionProperty->getStartLine(),
'endLine' => $reflectionProperty->getEndLine()
];
}
print_r($properties);
The snippet above will also get the property declaration when that property is declared in a trait or parent class. Obviously, this can be optimized, as it's splitting the source inside the loop.
use my\Project\FooClass;
$obj = new FooClass(); // ok
$name = 'FooClass';
$obj2 = new $name(); // throws an error that the class wasn't found
Well, I believe the title and the example were pretty enough explanation of my question, so just - why does this throws an error, and how should I deal with this?
Sadly, this is not possible due to the way PHP imports/aliases from namespaces. This can be remedied by using literal namespace definitions, though it no doubt sucks.
As follows:
$r = "my\\Project\\FooClass";
$k = new $r();
There is a patch in the works, or at the very least, it was on PHP's bug report a couple of months back. They will hopefully do something about it.
If it bothers you, you can use class_alias() to remedy it, by the way.
try:
$obj2 = new $name;
Remove the parenthesis
Alternatively:
$obj2 = new {$name}();
Can't explain why this doesn't work. But for how to deal with it:
$name = 'FooClass';
$name = "my\\Project\\FooClass\\" . $name; // prepend namespace
$obj2 = new $name();