]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/StubObject.php
MediaWiki 1.30.2-scripts2
[autoinstalls/mediawiki.git] / includes / StubObject.php
1 <?php
2 /**
3  * Delayed loading of global objects.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  * http://www.gnu.org/copyleft/gpl.html
19  *
20  * @file
21  */
22
23 /**
24  * Class to implement stub globals, which are globals that delay loading the
25  * their associated module code by deferring initialisation until the first
26  * method call.
27  *
28  * Note on reference parameters:
29  *
30  * If the called method takes any parameters by reference, the __call magic
31  * here won't work correctly. The solution is to unstub the object before
32  * calling the method.
33  *
34  * Note on unstub loops:
35  *
36  * Unstub loops (infinite recursion) sometimes occur when a constructor calls
37  * another function, and the other function calls some method of the stub. The
38  * best way to avoid this is to make constructors as lightweight as possible,
39  * deferring any initialisation which depends on other modules. As a last
40  * resort, you can use StubObject::isRealObject() to break the loop, but as a
41  * general rule, the stub object mechanism should be transparent, and code
42  * which refers to it should be kept to a minimum.
43  */
44 class StubObject {
45         /** @var null|string */
46         protected $global;
47
48         /** @var null|string */
49         protected $class;
50
51         /** @var null|callable */
52         protected $factory;
53
54         /** @var array */
55         protected $params;
56
57         /**
58          * @param string $global Name of the global variable.
59          * @param string|callable $class Name of the class of the real object
60          *                               or a factory function to call
61          * @param array $params Parameters to pass to constructor of the real object.
62          */
63         public function __construct( $global = null, $class = null, $params = [] ) {
64                 $this->global = $global;
65                 if ( is_callable( $class ) ) {
66                         $this->factory = $class;
67                 } else {
68                         $this->class = $class;
69                 }
70                 $this->params = $params;
71         }
72
73         /**
74          * Returns a bool value whenever $obj is a stub object. Can be used to break
75          * a infinite loop when unstubbing an object.
76          *
77          * @param object $obj Object to check.
78          * @return bool True if $obj is not an instance of StubObject class.
79          */
80         public static function isRealObject( $obj ) {
81                 return is_object( $obj ) && !$obj instanceof StubObject;
82         }
83
84         /**
85          * Unstubs an object, if it is a stub object. Can be used to break a
86          * infinite loop when unstubbing an object or to avoid reference parameter
87          * breakage.
88          *
89          * @param object &$obj Object to check.
90          * @return void
91          */
92         public static function unstub( &$obj ) {
93                 if ( $obj instanceof StubObject ) {
94                         $obj = $obj->_unstub( 'unstub', 3 );
95                 }
96         }
97
98         /**
99          * Function called if any function exists with that name in this object.
100          * It is used to unstub the object. Only used internally, PHP will call
101          * self::__call() function and that function will call this function.
102          * This function will also call the function with the same name in the real
103          * object.
104          *
105          * @param string $name Name of the function called
106          * @param array $args Arguments
107          * @return mixed
108          */
109         public function _call( $name, $args ) {
110                 $this->_unstub( $name, 5 );
111                 return call_user_func_array( [ $GLOBALS[$this->global], $name ], $args );
112         }
113
114         /**
115          * Create a new object to replace this stub object.
116          * @return object
117          */
118         public function _newObject() {
119                 $params = $this->factory
120                         ? [ 'factory' => $this->factory ]
121                         : [ 'class' => $this->class ];
122                 return ObjectFactory::getObjectFromSpec( $params + [
123                         'args' => $this->params,
124                         'closure_expansion' => false,
125                 ] );
126         }
127
128         /**
129          * Function called by PHP if no function with that name exists in this
130          * object.
131          *
132          * @param string $name Name of the function called
133          * @param array $args Arguments
134          * @return mixed
135          */
136         public function __call( $name, $args ) {
137                 return $this->_call( $name, $args );
138         }
139
140         /**
141          * This function creates a new object of the real class and replace it in
142          * the global variable.
143          * This is public, for the convenience of external callers wishing to access
144          * properties, e.g. eval.php
145          *
146          * @param string $name Name of the method called in this object.
147          * @param int $level Level to go in the stack trace to get the function
148          *   who called this function.
149          * @return object The unstubbed version of itself
150          * @throws MWException
151          */
152         public function _unstub( $name = '_unstub', $level = 2 ) {
153                 static $recursionLevel = 0;
154
155                 if ( !$GLOBALS[$this->global] instanceof StubObject ) {
156                         return $GLOBALS[$this->global]; // already unstubbed.
157                 }
158
159                 if ( get_class( $GLOBALS[$this->global] ) != $this->class ) {
160                         $caller = wfGetCaller( $level );
161                         if ( ++$recursionLevel > 2 ) {
162                                 throw new MWException( "Unstub loop detected on call of "
163                                         . "\${$this->global}->$name from $caller\n" );
164                         }
165                         wfDebug( "Unstubbing \${$this->global} on call of "
166                                 . "\${$this->global}::$name from $caller\n" );
167                         $GLOBALS[$this->global] = $this->_newObject();
168                         --$recursionLevel;
169                         return $GLOBALS[$this->global];
170                 }
171         }
172 }
173
174 /**
175  * Stub object for the user language. Assigned to the $wgLang global.
176  */
177 class StubUserLang extends StubObject {
178
179         public function __construct() {
180                 parent::__construct( 'wgLang' );
181         }
182
183         /**
184          * Call Language::findVariantLink after unstubbing $wgLang.
185          *
186          * This method is implemented with a full signature rather than relying on
187          * __call so that the pass-by-reference signature of the proxied method is
188          * honored.
189          *
190          * @param string &$link The name of the link
191          * @param Title &$nt The title object of the link
192          * @param bool $ignoreOtherCond To disable other conditions when
193          *   we need to transclude a template or update a category's link
194          */
195         public function findVariantLink( &$link, &$nt, $ignoreOtherCond = false ) {
196                 global $wgLang;
197                 $this->_unstub( 'findVariantLink', 3 );
198                 $wgLang->findVariantLink( $link, $nt, $ignoreOtherCond );
199         }
200
201         /**
202          * @return Language
203          */
204         public function _newObject() {
205                 return RequestContext::getMain()->getLanguage();
206         }
207 }