Merge "Http::getProxy() method to get proxy configuration"
[lhc/web/wiklou.git] / includes / libs / ObjectFactory.php
index 0b9aa7c..c96a8a1 100644 (file)
@@ -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;
+       }
 }