Completed
Branch dev (1ad2e5)
by
unknown
04:33
created

AdminPageFramework_Factory_Router   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 483
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 8
Bugs 0 Features 1
Metric Value
c 8
b 0
f 1
dl 0
loc 483
rs 8.2608
wmc 40
lcom 1
cbo 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A _replyToDetermineToLoad() 0 17 2
A _getFormObject() 0 11 1
A _getLinkObject() 0 3 1
A _getPageLoadObject() 0 3 1
A _replyTpSetAndGetInstance_oHeadTag() 0 4 1
B __construct() 0 36 5
B _replyToLoadComponents() 0 19 5
A _isInstantiatable() 0 3 1
A _isInThePage() 0 3 1
A __get() 0 10 2
A _replyTpSetAndGetInstance_oUtil() 0 5 1
A _replyTpSetAndGetInstance_oDebug() 0 5 1
A _replyTpSetAndGetInstance_oMsg() 0 7 1
A _replyTpSetAndGetInstance_oForm() 0 4 1
A _replyTpSetAndGetInstance_oResource() 0 8 2
A _replyTpSetAndGetInstance_oHelpPane() 0 5 1
A _replyTpSetAndGetInstance_oLink() 0 4 1
A _replyTpSetAndGetInstance_oPageLoadInfo() 0 4 1
A __call() 0 19 4
A _getAutoCallback() 0 18 3
A _triggerUndefinedMethodWarning() 0 10 1
A __toString() 0 3 1
A setFooterInfoRight() 0 1 1
A setFooterInfoLeft() 0 1 1

How to fix   Complexity   

Complex Class

Complex classes like AdminPageFramework_Factory_Router often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AdminPageFramework_Factory_Router, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Admin Page Framework
4
 * 
5
 * http://en.michaeluno.jp/admin-page-framework/
6
 * Copyright (c) 2013-2016 Michael Uno; Licensed MIT
7
 * 
8
 */
9
10
/**
11
 * Provides routing functionality to the Admin Page Framework factory object based on the fields type.
12
 * 
13
 * This class mainly deals with routing function calls and instantiation of objects based on the type.
14
 * 
15
 * @abstract
16
 * @since       3.0.4
17
 * @package     AdminPageFramework
18
 * @subpackage  Common/Factory
19
 * @internal
20
 */
21
abstract class AdminPageFramework_Factory_Router {
22
    
23
    /**
24
     * Stores the property object.
25
     * 
26
     * @since       2.0.0
27
     * @access      public      The AdminPageFramework_Page_MetaBox class accesses it.
28
     */     
29
    public $oProp;    
30
    
31
    /**
32
     * The object that provides the debug methods. 
33
     * 
34
     * @internal
35
     * @access      public
36
     * @since       2.0.0
37
     * @since       3.1.0   Changed the scope to public from protected.
38
     */     
39
    public $oDebug;
40
    /**
41
     * Provides the utility methods. 
42
     * 
43
     * @internal
44
     * @since       2.0.0
45
     * @since       3.1.0     Changed the scope to public from protected.
46
     */         
47
    public $oUtil;
48
    /**
49
     * Provides the methods for text messages of the framework. 
50
     * 
51
     * @since       2.0.0
52
     * @since       3.1.0     Changed the scope to public from protected.
53
     * @access      public
54
     * @internal
55
     */         
56
    public $oMsg;
57
    
58
    /**
59
     * The form object that provides methods to handle form sections and fields.
60
     * @internal
61
     * @since       3.0.0
62
     * @since       3.5.2       Changed the scope to public from protected as the widget class needs to initialize this object.
63
     */     
64
    public $oForm;
65
    
66
    /**
67
     * Inserts page load information into the footer area of the page. 
68
     * 
69
     */
70
    protected $oPageLoadInfo;
71
    
72
    /**
73
     * Provides the methods to insert head tag elements.
74
     * 
75
     * @since   3.3.0   Changed the name from $oHeadTag as it has become to deal with footer elements.
76
     */
77
    protected $oResource;
78
    
79
    /**
80
     * Provides the methods to insert head tag elements.
81
     * @deprecated
82
     */
83
    protected $oHeadTag;
84
    
85
    /**
86
     * Provides methods to manipulate contextual help pane.
87
     */
88
    protected $oHelpPane;
89
    
90
    /**
91
     * Provides the methods for creating HTML link elements. 
92
     * 
93
     */    
94
    protected $oLink;
95
    
96
    /**
97
     * Stores sub-class names.
98
     * 
99
     * Used in the __get() method to check whether a method with the name of the property should be called or not.
100
     * 
101
     * @since       3.7.12      Renamed from `_aSubClassNames`.
102
     * @internal
103
     * @remark      `$oProp` is not listed as it is created prior to calling the constructor of this class.
104
     */
105
    protected $_aSubClassPrefixes = array(
106
        'oForm'             => 'AdminPageFramework_Form_',
107
        'oPageLoadInfo'     => 'AdminPageFramework_PageLoadInfo_',
108
        'oResource'         => 'AdminPageFramework_Resource_',
109
        'oHelpPane'         => 'AdminPageFramework_HelpPane_',
110
        'oLink'             => 'AdminPageFramework_Link_',
111
    );
112
    
113
    /**
114
     * Stores the sub-object class names.
115
     * @since       3.5.3
116
     * @since       3.7.12      Changed the scope to private.
117
     */
118
    private $_aSubClassNames = array(
119
        'oProp'             => null,
120
        'oDebug'            => 'AdminPageFramework_Debug',
121
        'oUtil'             => 'AdminPageFramework_FrameworkUtility',
122
        'oMsg'              => 'AdminPageFramework_Message',
123
        'oForm'             => null,
124
        'oPageLoadInfo'     => null,
125
        'oResource'         => null,
126
        'oHelpPane'         => null,
127
        'oLink'             => null,
128
    );
129
    
130
    /**
131
     * Stores user-set sub-object class names.
132
     * 
133
     * This is for the user to use own classes for sub-class objects.
134
     * @since       3.7.12
135
     */
136
    public $aSubClassNames = array();
137
    
138
    /**
139
     * Sets up built-in objects.
140
     */
141
    public function __construct( $oProp ) {
142
143
        // Set sub-class names.
144
        foreach( $this->_aSubClassPrefixes as $_sObjectVariableName => $_sPrefix ) {
145
            $this->aSubClassNames[ $_sObjectVariableName ] = $_sPrefix . $this->_sStructureType;
0 ignored issues
show
Documentation introduced by
The property _sStructureType does not exist on object<AdminPageFramework_Factory_Router>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
146
        }
147
        $this->aSubClassNames = $this->aSubClassNames + $this->_aSubClassNames;
148
    
149
        // Let them overload so that these sub-class objects will not be instantiated until they are required.
150
        unset( 
151
            $this->oDebug, 
152
            $this->oUtil, 
153
            $this->oMsg, 
154
            $this->oForm, 
155
            $this->oPageLoadInfo,
156
            $this->oResource,
157
            $this->oHelpPane,
158
            $this->oLink
159
        );
160
        
161
        // Required sub-class objects
162
        $this->oProp = $oProp;
163
            
164
        if ( $this->oProp->bIsAdmin && ! $this->oProp->bIsAdminAjax ) {
165
            if ( did_action( 'current_screen' ) ) {
166
                $this->_replyToLoadComponents();
167
            } else {                
168
                add_action( 'current_screen', array( $this, '_replyToLoadComponents' ) );
169
            }
170
        }
171
        
172
        // Call the user constructor.
173
        $this->start();     // defined in the controller class.
0 ignored issues
show
Documentation Bug introduced by
The method start does not exist on object<AdminPageFramework_Factory_Router>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
174
        $this->oUtil->addAndDoAction( $this, 'start_' . $this->oProp->sClassName, $this );
175
        
176
    }    
177
        
178
    /**
179
     * Determines whether the class component classes should be instantiated or not.
180
     * 
181
     * @internal
182
     * @callback    action      current_screen
183
     * @return      void
184
     */
185
    public function _replyToLoadComponents( /* $oScreen */ ) {
186
187
        if ( ! $this->_isInThePage() ) { 
188
            return; 
189
        }
190
                    
191
        if ( ! isset( $this->oResource ) ) {
192
            $this->oResource = $this->_replyTpSetAndGetInstance_oResource();
193
        }
194
        
195
        if ( ! isset(  $this->oLink ) ) {
196
            $this->oLink = $this->_replyTpSetAndGetInstance_oLink();         
197
        }
198
        
199
        if ( $this->oUtil->isDebugMode() ) {
200
            $this->oPageLoadInfo = $this->oPageLoadInfo;
201
        }
202
        
203
    }
204
205
206
    /**
207
     * Determines whether the class object is instantiatable in the current page.
208
     * 
209
     * This method should be redefined in the extended class.
210
     * 
211
     * @since       3.1.0
212
     * @internal
213
     */ 
214
    protected function _isInstantiatable() { 
215
        return true; 
216
    }
217
    
218
    /**
219
     * Determines whether the instantiated object and its producing elements belong to the loading page.
220
     * 
221
     * This method should be redefined in the extended class.
222
     * 
223
     * @remark      This method should be called AFTER current screen is determined such as after the `current_screen` action hook.
224
     * @since       3.0.3
225
     * @since       3.2.0   Changed the visibility scope to `public` from `protected` as the head tag object will access it.
226
     * @todo        Change the visibility scope to `protected` as the public version of the method `isInThePage()` has been introduced to make the design consitent.
227
     * @internal
228
     */
229
    public function _isInThePage() { 
230
        return true; 
231
    }
232
         
233
    /**
234
     * Determines whether the `setUp()` method should be called.
235
     * 
236
     * @since       3.7.10
237
     * @callback    
238
     * @internal    
239
     * @return      void
240
     */
241
    public function _replyToDetermineToLoad() {
242
243
        if ( ! $this->_isInThePage() ) { 
244
            return; 
245
        }
246
247
        // Calls `setUp()` and the user will set up the meta box.
248
        $this->_setUp();
0 ignored issues
show
Documentation Bug introduced by
The method _setUp does not exist on object<AdminPageFramework_Factory_Router>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
249
        
250
        /**
251
         * This action hook must be called AFTER the _setUp() method 
252
         * as there are callback methods that hook into this hook 
253
         * and assumes required configurations have been made.
254
         */
255
        $this->oUtil->addAndDoAction( $this, "set_up_{$this->oProp->sClassName}", $this );
256
                          
257
    }          
258
         
259
         
260
    /**
261
     * Instantiate a form object based on the type.
262
     * 
263
     * @since       3.1.0
264
     * @internal
265
     * @return      object|null
266
     */
267
    protected function _getFormObject() {
268
    
269
        $this->oProp->setFormProperties();
270
        $_sFormClass = $this->aSubClassNames[ 'oForm' ];
271
        return new $_sFormClass(
272
            $this->oProp->aFormArguments, // Options - for the values that do not need to change through out the script execution. 
273
            $this->oProp->aFormCallbacks, // Callbacks - for the values which change dynamically depending on conditions such as the loaded page url.
274
            $this->oMsg
275
        );    
276
        
277
    }
278
     
279
    /**
280
     * Instantiates a link object based on the type.
281
     * 
282
     * @since       3.0.4
283
     * @since       3.7.10      Removed the parameters as those values will be set in the extended class.
284
     * @remark      Override this method in an extended class.
285
     * @internal
286
     * @return      null|object
287
     */
288
    protected function _getLinkObject() {
289
        return null;
290
    }
291
    
292
    /**
293
     * Instantiates a page load object based on the type.
294
     * 
295
     * @since       3.0.4
296
     * @since       3.7.10      Removed the parameters as those values will be set in the extended class.
297
     * @internal
298
     */
299
    protected function _getPageLoadObject() {
300
        return null;
301
    }
302
      
303
    /**
304
     * Responds to a request of an undefined property.
305
     * 
306
     * This is used to instantiate classes only when necessary, rather than instantiating them all at once.
307
     * 
308
     * @internal
309
     */
310
    public function __get( $sPropertyName ) {
311
            
312
        // Set and return the sub class object instance.
313
        if ( isset( $this->aSubClassNames[ $sPropertyName ] ) ) {
314
            return call_user_func( 
315
                array( $this, "_replyTpSetAndGetInstance_{$sPropertyName}"  )
316
            );
317
        }
318
        
319
    }
320
        /**#@+
321
         * @internal
322
         * @return      object
323
         * @callback    function    call_user_func
324
         */          
325
        /**
326
         * Sets and returns the `oUtil` property.
327
         * @since       3.5.3
328
         */
329
        public function _replyTpSetAndGetInstance_oUtil() {
330
            $_sClassName = $this->aSubClassNames[ 'oUtil' ];
331
            $this->oUtil = new $_sClassName;
332
            return $this->oUtil;
333
        }
334
        /**
335
         * Sets and returns the `oDebug` property.
336
         * @since       3.5.3
337
         */        
338
        public function _replyTpSetAndGetInstance_oDebug() {
339
            $_sClassName = $this->aSubClassNames[ 'oDebug' ];
340
            $this->oDebug = new $_sClassName;
341
            return $this->oDebug;
342
        }
343
        /**
344
         * Sets and returns the `oMsg` property.
345
         * @since       3.5.3
346
         */              
347
        public function _replyTpSetAndGetInstance_oMsg() {
348
            $this->oMsg = call_user_func_array(
349
                array( $this->aSubClassNames[ 'oMsg' ], 'getInstance'),
350
                array( $this->oProp->sTextDomain )  // parameters
351
            );
352
            return $this->oMsg;
353
        }
354
        /**
355
         * Sets and returns the `oForm` property.
356
         * @since       3.5.3
357
         */              
358
        public function _replyTpSetAndGetInstance_oForm() {
359
            $this->oForm = $this->_getFormObject();           
360
            return $this->oForm;
361
        }
362
        /**
363
         * Sets and returns the `oResouce` property.
364
         * @since       3.5.3
365
         */            
366
        public function _replyTpSetAndGetInstance_oResource() {
367
            if ( isset( $this->oResource ) ) {
368
                return $this->oResource;
369
            }
370
            $_sClassName     = $this->aSubClassNames[ 'oResource' ];
371
            $this->oResource = new $_sClassName( $this->oProp );
372
            return $this->oResource;
373
        }
374
            /**
375
             * Kept for backward compatibility.
376
             * @since       3.7.10
377
             */
378
            public function _replyTpSetAndGetInstance_oHeadTag() {
379
                $this->oHead = $this->_replyTpSetAndGetInstance_oResource();
0 ignored issues
show
Bug introduced by
The property oHead does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
380
                return $this->oHead;
381
            }        
382
        /**
383
         * Sets and returns the `oHelpPane` property.
384
         * @since       3.5.3
385
         */
386
        public function _replyTpSetAndGetInstance_oHelpPane() {
387
            $_sClassName     = $this->aSubClassNames[ 'oHelpPane' ];
388
            $this->oHelpPane = new $_sClassName( $this->oProp );            
389
            return $this->oHelpPane;
390
        }
391
        /**
392
         * Sets and returns the `oLink` property.
393
         * @since       3.5.3
394
         */
395
        public function _replyTpSetAndGetInstance_oLink() {
396
            $this->oLink = $this->_getLinkObject();
397
            return $this->oLink;
398
        }
399
        /**
400
         * Sets and returns the `oPageLoadInfo` property.
401
         * @since       3.5.3
402
         */        
403
        public function _replyTpSetAndGetInstance_oPageLoadInfo() {
404
            $this->oPageLoadInfo = $this->_getPageLoadObject();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $this->oPageLoadInfo is correct as $this->_getPageLoadObject() (which targets AdminPageFramework_Facto...r::_getPageLoadObject()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
405
            return $this->oPageLoadInfo;
406
        }
407
        /**#@-*/
408
        
409
    /**
410
     * Redirects dynamic function calls to the pre-defined internal method.
411
     * 
412
     * @internal
413
     */
414
    public function __call( $sMethodName, $aArguments=null ) {    
415
         
416
        $_mFirstArg = $this->oUtil->getElement( $aArguments, 0 );
417
        
418
        switch ( $sMethodName ) {
419
            case 'validate':
420
            case 'content':
421
                return $_mFirstArg;
422
                
423
        }
424
        
425
        // If it is called with the framework auto-callback,
426
        if ( has_filter( $sMethodName ) ) {
427
            return $this->_getAutoCallback( $sMethodName, $aArguments );
428
        }
429
                
430
        $this->_triggerUndefinedMethodWarning( $sMethodName );
431
        
432
    }     
433
        /**
434
         * Returns the first parameter value if the method name does not contain a backslash.
435
         * If it contains a backslash, the user uses a name-spaced class name. In that case,
436
         * the backslashes need to be converted to underscores to support valid PHP method names.
437
         * 
438
         * @since       3.7.0
439
         */
440
        private function _getAutoCallback( $sMethodName, $aArguments ) {
441
            
442
            // Check if the method name does not contain a backslash.
443
            if ( false === strpos( $sMethodName, "\\" ) ) {
444
                return $this->oUtil->getElement( $aArguments, 0 );  // the first element - the filter value
445
            }
446
                
447
            // If the method name contains a backslash, the user may be using a name space. 
448
            // In that case, convert the backslash to underscore and call the method.
449
            $_sAutoCallbackMethodName = str_replace( '\\', '_', $sMethodName );
450
            return method_exists( $this, $_sAutoCallbackMethodName )
451
                ? call_user_func_array(
452
                    array( $this, $_sAutoCallbackMethodName ),
453
                    $aArguments
454
                )
455
                : $this->oUtil->getElement( $aArguments, 0 );   // the first argument
456
            
457
        }
458
        
459
        /**
460
         * @since   3.7.0
461
         * @return  void
462
         */
463
        private function _triggerUndefinedMethodWarning( $sMethodName ) {
464
            trigger_error(
465
                AdminPageFramework_Registry::NAME . ': ' 
466
                    . sprintf( 
467
                        __( 'The method is not defined: %1$s', $this->oProp->sTextDomain ),
468
                        $sMethodName 
469
                    ), 
470
                E_USER_WARNING 
471
            );            
472
        }
473
            
474
        
475
    
476
    /**
477
     * Prevents the output from getting too long when the object is dumped.
478
     *
479
     * Field definition arrays contain the factory object reference and when the debug log method tries to dump it, the output gets too long.
480
     * So shorten it here.
481
     * 
482
     * @remark      Called when the object is called as a string.
483
     * @since       3.4.4
484
     */   
485
    public function __toString() {
486
        return $this->oUtil->getObjectInfo( $this );        
487
    }
488
 
489
    /**
490
     * Deprecated methods.
491
     */
492
    /**
493
     * @remark          This was not functional since 3.1.3
494
     * @deprecated      3.5.5
495
     */
496
    public function setFooterInfoRight() {}
497
    /**
498
     * @remark          This was not functional since 3.1.3
499
     * @deprecated      3.5.5
500
     */    
501
    public function setFooterInfoLeft() {}
502
 
503
}
504