A quick check to see if an object will be successfully created in PHP?

How can I check if an object will be successfully created with the given argument, without actually instantiating?

In fact, I only check (have not tested this code, but should work fine ...) the number of required parameters, ignoring the types:

// Filter definition and arguments as per configuration $filter = $container->getDefinition($serviceId); $args = $activeFilters[$filterName]; // Check number of required arguments vs arguments in config $constructor = $reflector->getConstructor(); $numRequired = $constructor->getNumberOfRequiredParameters(); $numSpecified = is_array($args) ? count($args) : 1; if($numRequired < $numSpecified) { throw new InvalidFilterDefinitionException( $serviceId, $numRequired, $numSpecified ); } 

EDIT : $constructor may be null ...

+4
source share
1 answer

The short answer is that you simply cannot determine whether the set of arguments will allow error-free instantiation of the constructor. As commentators have already pointed out, there is no way to know for sure whether it is possible to create an instance of a class with a given list of arguments, because there are run-time considerations that cannot be known without an actual attempt to specify.

However, it makes sense to try to instantiate the class from the constructor argument list. The most obvious use case for this kind of operation is a configurable dependency injection container (DIC). Unfortunately, this is a much more complicated operation than the OP offers.

We need to determine for each argument in the supplied array of definitions whether it matches the specified hint types from the signature of the constructor method (if the method signature actually has type hints). In addition, we need to decide how to handle the default argument values. In addition, in order for our code to have any real use, we need to specify "definitions" in advance to create an instance of the class. An improved solution to the problem will also include a pool of reflection objects (caching) in order to minimize the impact of performance on multiply reflective things.

Another obstacle is the fact that there is no way to access the tooltip of the parameter of the reflected method without calling its ReflectionParameter::getClass and then creating the reflection class from the returned class name (if null returns the parameter does not have a tooltip). This is where caching of generated reflections becomes especially important for any real use case.

The code below is a greatly truncated version of my own injection container with a recursive dependency based on strings. This is a mixture of pseudo code and real code (if you were hoping for a free copy / paste code, you're out of luck). You will see that the code below matches the keys of an associative array of definition arrays with the parameter names in the constructor signature.

The actual code can be found on the github page.

 class Provider { private $definitions; public function define($class, array $definition) { $class = strtolower($class); $this->definitions[$class] = $definition; } public function make($class, array $definition = null) { $class = strtolower($class); if (is_null($definition) && isset($this->definitions[$class])) { $definition = $this->definitions[$class]; } $reflClass = new ReflectionClass($class); $instanceArgs = $this->buildNewInstanceArgs($reflClass); return $reflClass->newInstanceArgs($instanceArgs); } private function buildNewInstanceArgs( ReflectionClass $reflClass, array $definition ) { $instanceArgs = array(); $reflCtor = $reflClass->getConstructor(); // IF no constructor exists we're done and should just // return a new instance of $class: // return $this->make($reflClass->name); // otherwise ... $reflCtorParams = $reflCtor->getParameters(); foreach ($reflCtorParams as $ctorParam) { if (isset($definition[$ctorParam->name])) { $instanceArgs[] = $this->make($definition[$ctorParam->name]); continue; } $typeHint = $this->getParameterTypeHint($ctorParam); if ($typeHint && $this->isInstantiable($typeHint)) { // The typehint is instantiable, go ahead and make a new // instance of it $instanceArgs[] = $this->make($typeHint); } elseif ($typeHint) { // The typehint is abstract or an interface. We can't // proceed because we already know we don't have a // definition telling us which class to instantiate throw Exception; } elseif ($ctorParam->isDefaultValueAvailable()) { // No typehint, try to use the default parameter value $instanceArgs[] = $ctorParam->getDefaultValue(); } else { // If all else fails, try passing in a NULL or something $instanceArgs[] = NULL; } } return $instanceArgs; } private function getParameterTypeHint(ReflectionParameter $param) { // ... see the note about retrieving parameter typehints // in the exposition ... } private function isInstantiable($class) { // determine if the class typehint is abstract/interface // RTM on reflection for how to do this } } 
+5
source

Source: https://habr.com/ru/post/1447781/


All Articles