X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Flibs%2FObjectFactory.php;h=c96a8a1643e78639dc6bc5aa006ecc458a5a3124;hb=e3bd13db0c285f312e31bb1b7271af4628cca80c;hp=0b9aa7ca60bbfaac509496d6c64aaa2ff4c22d08;hpb=f0b51b42007de9e30f97d2bfbde276053d32c579;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/libs/ObjectFactory.php b/includes/libs/ObjectFactory.php index 0b9aa7ca60..c96a8a1643 100644 --- a/includes/libs/ObjectFactory.php +++ b/includes/libs/ObjectFactory.php @@ -35,13 +35,6 @@ class ObjectFactory { * an 'args' key that provides arguments to pass to the * constructor/callable. * - * Object construction using a specification having both 'class' and - * 'args' members will call the constructor of the class using - * ReflectionClass::newInstanceArgs. The use of ReflectionClass carries - * a performance penalty and should not be used to create large numbers of - * objects. If this is needed, consider introducing a factory method that - * can be called via call_user_func_array() instead. - * * Values in the arguments collection which are Closure instances will be * expanded by invoking them with no arguments before passing the * resulting value on to the constructor/callable. This can be used to @@ -64,7 +57,7 @@ class ObjectFactory { * constructor is non-public or non-existent */ public static function getObjectFromSpec( $spec ) { - $args = isset( $spec['args'] ) ? $spec['args'] : array(); + $args = isset( $spec['args'] ) ? $spec['args'] : []; $expandArgs = !isset( $spec['closure_expansion'] ) || $spec['closure_expansion'] === true; @@ -77,8 +70,7 @@ class ObjectFactory { if ( !$args ) { $obj = new $clazz(); } else { - $ref = new ReflectionClass( $clazz ); - $obj = $ref->newInstanceArgs( $args ); + $obj = static::constructClassInstance( $clazz, $args ); } } elseif ( isset( $spec['factory'] ) ) { $obj = call_user_func_array( $spec['factory'], $args ); @@ -94,7 +86,7 @@ class ObjectFactory { if ( $expandArgs ) { $margs = static::expandClosures( $margs ); } - call_user_func_array( array( $obj, $method ), $margs ); + call_user_func_array( [ $obj, $method ], $margs ); } } @@ -117,4 +109,91 @@ class ObjectFactory { } }, $list ); } + + /** + * Construct an instance of the given class using the given arguments. + * + * PHP's `call_user_func_array()` doesn't work with object construction so + * we have to use other measures. Starting with PHP 5.6.0 we could use the + * "splat" operator (`...`) to unpack the array into an argument list. + * Sadly there is no way to conditionally include a syntax construct like + * a new operator in a way that allows older versions of PHP to still + * parse the file. Instead, we will try a loop unrolling technique that + * works for 0-10 arguments. If we are passed 11 or more arguments we will + * take the performance penalty of using + * `ReflectionClass::newInstanceArgs()` to construct the desired object. + * + * @param string $clazz Class name + * @param array $args Constructor arguments + * @return mixed Constructed instance + */ + public static function constructClassInstance( $clazz, $args ) { + // $args should be a non-associative array; show nice error if that's not the case + if ( $args && array_keys( $args ) !== range( 0, count( $args ) - 1 ) ) { + throw new InvalidArgumentException( __METHOD__ . ': $args cannot be an associative array' ); + } + + // TODO: when PHP min version supported is >=5.6.0 replace this + // with `return new $clazz( ... $args );`. + $obj = null; + switch ( count( $args ) ) { + case 0: + $obj = new $clazz(); + break; + case 1: + $obj = new $clazz( $args[0] ); + break; + case 2: + $obj = new $clazz( $args[0], $args[1] ); + break; + case 3: + $obj = new $clazz( $args[0], $args[1], $args[2] ); + break; + case 4: + $obj = new $clazz( $args[0], $args[1], $args[2], $args[3] ); + break; + case 5: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4] + ); + break; + case 6: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4], + $args[5] + ); + break; + case 7: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4], + $args[5], $args[6] + ); + break; + case 8: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4], + $args[5], $args[6], $args[7] + ); + break; + case 9: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4], + $args[5], $args[6], $args[7], $args[8] + ); + break; + case 10: + $obj = new $clazz( + $args[0], $args[1], $args[2], $args[3], $args[4], + $args[5], $args[6], $args[7], $args[8], $args[9] + ); + break; + default: + // Fall back to using ReflectionClass and curse the developer + // who decided that 11+ args was a reasonable method + // signature. + $ref = new ReflectionClass( $clazz ); + $obj = $ref->newInstanceArgs( $args ); + } + return $obj; + } }