GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Module   B
last analyzed

Complexity

Total Complexity 52

Size/Duplication

Total Lines 539
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 52
lcom 1
cbo 5
dl 0
loc 539
rs 7.9487
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A get_id() 0 4 1
A get_path() 0 4 1
A get_descriptor() 0 4 1
A get_collection() 0 4 1
A __construct() 0 5 1
A __toString() 0 4 1
A get_flat_id() 0 9 1
A get_model() 0 4 1
A get_title() 0 6 2
A get_parent() 0 4 1
B is_installed() 0 25 4
B install() 0 37 5
B uninstall() 0 26 5
B model() 0 58 6
F resolve_model_tags() 0 134 19
A getBlock() 0 21 2

How to fix   Complexity   

Complex Class

Complex classes like Module 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 Module, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace ICanBoogie;
13
14
use ICanBoogie\ActiveRecord\Connection;
15
use ICanBoogie\ActiveRecord\Model;
16
use ICanBoogie\ActiveRecord\ModelNotDefined;
17
use ICanBoogie\I18n;
18
use ICanBoogie\Module\Descriptor;
19
use ICanBoogie\Module\ModuleCollection;
20
21
/**
22
 * A module of the framework.
23
 *
24
 * @property-read array $descriptor The descriptor of the module.
25
 * @property-read string $flat_id Underscored identifier.
26
 * @property-read string $id The identifier of the module, defined by {@link Descriptor::ID}.
27
 * @property-read Model $model The primary model of the module.
28
 * @property-read Module $parent The parent module, defined by {@link Descriptor::INHERITS}.
29
 * @property-read string $path The path to the module, defined by {@link Descriptor::PATH}.
30
 * @property-read string $title The localized title of the module.
31
 * @property-read ModuleCollection $collection
32
 * @property-read Core|Module\CoreBindings|Binding\ActiveRecord\CoreBindings|Binding\I18n\CoreBindings $app
33
 */
34
class Module extends Prototyped
35
{
36
	/*
37
	 * PERMISSIONS:
38
	 *
39
	 * NONE: Well, you can't do anything
40
	 *
41
	 * ACCESS: You can access the module and view its records
42
	 *
43
	 * CREATE: You can create new records
44
	 *
45
	 * MAINTAIN: You can edit the records you created
46
	 *
47
	 * MANAGE: You can delete the records you created
48
	 *
49
	 * ADMINISTER: You have complete control over the module
50
	 *
51
	 */
52
	const PERMISSION_NONE = 0;
53
	const PERMISSION_ACCESS = 1;
54
	const PERMISSION_CREATE = 2;
55
	const PERMISSION_MAINTAIN = 3;
56
	const PERMISSION_MANAGE = 4;
57
	const PERMISSION_ADMINISTER = 5;
58
59
	/**
60
	 * Defines the name of the operation used to save the records of the module.
61
	 */
62
	const OPERATION_SAVE = 'save';
63
64
	/**
65
	 * Defines the name of the operation used to delete the records of the module.
66
	 */
67
	const OPERATION_DELETE = 'delete';
68
69
	/**
70
	 * Returns the identifier of the module as defined by its descriptor.
71
	 *
72
	 * This method is the getter for the {@link $id} magic property.
73
	 *
74
	 * @return string
75
	 */
76
	protected function get_id()
77
	{
78
		return $this->descriptor[Descriptor::ID];
79
	}
80
81
	/**
82
	 * Returns the path of the module as defined by its descriptor.
83
	 *
84
	 * This method is the getter for the {@link $path} magic property.
85
	 *
86
	 * @return string
87
	 */
88
	protected function get_path()
89
	{
90
		return $this->descriptor[Descriptor::PATH];
91
	}
92
93
	/**
94
	 * The descriptor of the module.
95
	 *
96
	 * @var array
97
	 */
98
	protected $descriptor;
99
100
	/**
101
	 * Returns the descriptor of the module.
102
	 *
103
	 * This method is the getter for the {@link $descriptor} magic property.
104
	 *
105
	 * @return array
106
	 */
107
	protected function get_descriptor()
108
	{
109
		return $this->descriptor;
110
	}
111
112
	/**
113
	 * Cache for loaded models.
114
	 *
115
	 * @var ActiveRecord\Model[]
116
	 */
117
	private $models = [];
118
119
	/**
120
	 * @var ModuleCollection
121
	 */
122
	private $collection;
123
124
	/**
125
	 * @return ModuleCollection
126
	 */
127
	protected function get_collection()
128
	{
129
		return $this->collection;
130
	}
131
132
	/**
133
	 * Constructor.
134
	 *
135
	 * Initializes the {@link $descriptor} property.
136
	 *
137
	 * @param ModuleCollection $collection
138
	 * @param array $descriptor
139
	 */
140
	public function __construct(ModuleCollection $collection, array $descriptor)
141
	{
142
		$this->collection = $collection;
143
		$this->descriptor = $descriptor;
144
	}
145
146
	/**
147
	 * Returns the identifier of the module.
148
	 *
149
	 * @return string
150
	 */
151
	public function __toString()
152
	{
153
		return $this->id;
154
	}
155
156
	/**
157
	 * Returns the _flat_ version of the module's identifier.
158
	 *
159
	 * This method is the getter for the {@link $flat_id} magic property.
160
	 *
161
	 * @return string
162
	 */
163
	protected function get_flat_id()
164
	{
165
		return strtr($this->id, [
166
167
			'.' => '_',
168
			'-' => '_'
169
170
		]);
171
	}
172
173
	/**
174
	 * Returns the primary model of the module.
175
	 *
176
	 * This is the getter for the {@link $model} magic property.
177
	 *
178
	 * @return ActiveRecord\Model
179
	 */
180
	protected function get_model()
181
	{
182
		return $this->model();
183
	}
184
185
	/**
186
	 * Returns the module title, translated to the current language.
187
	 *
188
	 * @return string
189
	 *
190
	 * @deprecated
191
	 */
192
	protected function get_title()
193
	{
194
		$default = isset($this->descriptor[Descriptor::TITLE]) ? $this->descriptor[Descriptor::TITLE] : 'Undefined';
195
196
		return $this->app->translate($this->flat_id, [], [ 'scope' => 'module_title', 'default' => $default ]);
0 ignored issues
show
Bug introduced by
The method translate does only exist in ICanBoogie\Binding\I18n\CoreBindings, but not in ICanBoogie\Binding\Activ...gie\Module\CoreBindings.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
197
	}
198
199
	/**
200
	 * Returns the parent module.
201
	 *
202
	 * @return Module|null
203
	 */
204
	protected function get_parent()
205
	{
206
		return $this->descriptor[Descriptor::INHERITS];
207
	}
208
209
	/**
210
	 * Checks if the module is installed.
211
	 *
212
	 * @param ErrorCollection $errors Error collection.
213
	 *
214
	 * @return mixed `true` if the module is installed, `false` if the module
215
	 * (or parts of) is not installed, `null` if the module has no installation.
216
	 */
217
	public function is_installed(ErrorCollection $errors)
218
	{
219
		if (empty($this->descriptor[Descriptor::MODELS]))
220
		{
221
			return null;
222
		}
223
224
		$rc = true;
225
226
		foreach ($this->descriptor[Descriptor::MODELS] as $name => $tags)
227
		{
228
			if (!$this->model($name)->is_installed())
229
			{
230
				$errors->add($this->id, "The model %name is not installed.", [
231
232
					'name' => $name
233
234
				]);
235
236
				$rc = false;
237
			}
238
		}
239
240
		return $rc;
241
	}
242
243
	/**
244
	 * Install the module.
245
	 *
246
	 * If the module has models they are installed.
247
	 *
248
	 * @param ErrorCollection $errors Error collection.
249
	 *
250
	 * @return boolean|null true if the module has successfully been installed, false if the
251
	 * module (or parts of the module) fails to install or null if the module has
252
	 * no installation process.
253
	 */
254
	public function install(ErrorCollection $errors)
255
	{
256
		if (empty($this->descriptor[Descriptor::MODELS]))
257
		{
258
			return null;
259
		}
260
261
		$rc = true;
262
263
		foreach ($this->descriptor[Descriptor::MODELS] as $name => $tags)
264
		{
265
			$model = $this->model($name);
266
267
			if ($model->is_installed())
268
			{
269
				continue;
270
			}
271
272
			try
273
			{
274
				$model->install();
275
			}
276
			catch (\Exception $e)
277
			{
278
				$errors->add($this->id, "Unable to install model %model: !message", [
279
280
					'model' => $name,
281
					'message' => $e->getMessage()
282
283
				]);
284
285
				$rc = false;
286
			}
287
		}
288
289
		return $rc;
290
	}
291
292
	/**
293
	 * Uninstall the module.
294
	 *
295
	 * Basically it uninstall the models installed by the module.
296
	 *
297
	 * @return boolean|null `true` if the module was successfully uninstalled. `false` if the module
298
	 * (or parts of the module) failed to uninstall. `null` if there is no uninstall process.
299
	 */
300
	public function uninstall()
301
	{
302
		if (empty($this->descriptor[Descriptor::MODELS]))
303
		{
304
			return null;
305
		}
306
307
		$rc = true;
308
309
		foreach ($this->descriptor[Descriptor::MODELS] as $name => $tags)
310
		{
311
			$model = $this->model($name);
312
313
			if (!$model->is_installed())
314
			{
315
				continue;
316
			}
317
318
			if (!$model->uninstall())
319
			{
320
				$rc = false;
321
			}
322
		}
323
324
		return $rc;
325
	}
326
327
	/**
328
	 * Get a model from the module.
329
	 *
330
	 * If the model has not been created yet, it is created on the fly.
331
	 *
332
	 * @param string $which The identifier of the model to get.
333
	 *
334
	 * @return Model The requested model.
335
	 *
336
	 * @throws ModelNotDefined when the model is not defined by the module.
337
	 * @throws \RuntimeException when the class of the model does not exists.
338
	 */
339
	public function model($which = 'primary')
340
	{
341
		if (empty($this->models[$which]))
342
		{
343
			if (empty($this->descriptor[Descriptor::MODELS][$which]))
344
			{
345
				throw new ModelNotDefined($which);
346
			}
347
348
			#
349
			# resolve model tags
350
			#
351
352
			$callback = "resolve_{$which}_model_tags";
353
354
			if (!method_exists($this, $callback))
355
			{
356
				$callback = 'resolve_model_tags';
357
			}
358
359
			$attributes = $this->$callback($this->descriptor[Descriptor::MODELS][$which], $which);
360
361
			#
362
			# COMPATIBILITY WITH 'inherit'
363
			#
364
365
			if ($attributes instanceof Model)
366
			{
367
				$this->models[$which] = $attributes;
368
369
				return $attributes;
370
			}
371
372
			#
373
			# create model
374
			#
375
376
			$class = $attributes[Model::CLASSNAME];
377
378
			if (!class_exists($class))
379
			{
380
				throw new \RuntimeException(\ICanBoogie\format("Unable to instantiate model %model, the class %class does not exists.", [
381
382
					'model' => "$this->id/$which",
383
					'class' => $class
384
385
				]));
386
			}
387
388
			$this->models[$which] = new $class($this->app->models, $attributes);
389
		}
390
391
		#
392
		# return cached model
393
		#
394
395
		return $this->models[$which];
396
	}
397
398
	/**
399
	 * Resolves model tags.
400
	 *
401
	 * @param array|string $tags
402
	 * @param string $which
403
	 *
404
	 * @return array
405
	 */
406
	protected function resolve_model_tags($tags, $which)
407
	{
408
		$app = $this->app;
409
410
		#
411
		# The model may use another model, in which case the model to use is defined using a
412
		# string e.g. 'contents' or 'terms/nodes'
413
		#
414
415
		if (is_string($tags))
416
		{
417
			$model_name = $tags;
418
419
			if ($model_name == 'inherit')
420
			{
421
				$class = get_parent_class($this);
422
423
				foreach ($app->modules->descriptors as $module_id => $descriptor)
424
				{
425
					if ($class != $descriptor['class'])
426
					{
427
						continue;
428
					}
429
430
					$model_name = $app->models[$module_id];
431
432
					break;
433
				}
434
			}
435
436
			$tags = [ Model::EXTENDING => $model_name ];
437
		}
438
439
		#
440
		# defaults
441
		#
442
443
		$id = $this->id;
444
445
		$tags += [
446
447
			Model::CONNECTION => 'primary',
448
			Model::ID => $which == 'primary' ? $id : $id . '/' . $which,
449
			Model::EXTENDING => null
450
451
		];
452
453
		if (empty($tags[Model::NAME]))
454
		{
455
			$tags[Model::NAME] = ModuleCollection::format_model_name($id, $which);
456
		}
457
458
		#
459
		# relations
460
		#
461
462
		if (isset($tags[Model::EXTENDING]))
463
		{
464
			$extends = &$tags[Model::EXTENDING];
465
466
			if (is_string($extends))
467
			{
468
				$extends = $this->app->models[$extends];
469
			}
470
471
			if (!$tags[Model::CLASSNAME])
472
			{
473
				$tags[Model::CLASSNAME] = get_class($extends);
474
			}
475
		}
476
477
		#
478
		#
479
		#
480
481
		if (isset($tags[Model::IMPLEMENTING]))
482
		{
483
			$implements =& $tags[Model::IMPLEMENTING];
484
485
			foreach ($implements as &$implement)
486
			{
487
				if (isset($implement['model']))
488
				{
489
					list($implement_id, $implement_which) = explode('/', $implement['model']) + [ 1 => 'primary' ];
490
491
					if ($id == $implement_id && $which == $implement_which)
492
					{
493
						throw new \RuntimeException(\ICanBoogie\format('Model %module/%model implements itself !', [
494
495
							'%module' => $id,
496
							'%model' => $which
497
498
						]));
499
					}
500
501
					$module = ($implement_id == $id) ? $this : \ICanBoogie\app()->modules[$implement_id];
502
503
					$implement['table'] = $module->model($implement_which);
504
				}
505
				else if (is_string($implement['table']))
506
				{
507
					throw new \RuntimeException(\ICanBoogie\format('Model %model of module %module implements a table: %table', [
508
509
						'%model' => $which,
510
						'%module' => $id,
511
						'%table' => $implement['table']
512
513
					]));
514
				}
515
			}
516
		}
517
518
		#
519
		# default class, if none was defined.
520
		#
521
522
		if (empty($tags[Model::CLASSNAME]))
523
		{
524
			$tags[Model::CLASSNAME] = 'ICanBoogie\ActiveRecord\Model';
525
		}
526
527
		#
528
		# connection
529
		#
530
531
		$connection = $tags[Model::CONNECTION];
532
533
		if (!($connection instanceof Connection))
534
		{
535
			$tags[Model::CONNECTION] = $this->app->connections[$connection];
536
		}
537
538
		return $tags;
539
	}
540
541
	/**
542
	 * Get a block.
543
	 *
544
	 * @param string $name The name of the block to get.
545
	 *
546
	 * @return mixed Depends on the implementation. Should return a string or an object
547
	 * implementing `__toString`.
548
	 *
549
	 * @throws \RuntimeException if the block is not defined.
550
	 */
551
	public function getBlock($name)
552
	{
553
		$args = func_get_args();
554
555
		array_shift($args);
556
557
		$callback = 'block_' . $name;
558
559
		if (!method_exists($this, $callback))
560
		{
561
			throw new \RuntimeException(\ICanBoogie\format('The %method method is missing from the %module module to create block %type.', [
562
563
				'%method' => $callback,
564
				'%module' => $this->id,
565
				'%type' => $name
566
567
			]));
568
		}
569
570
		return call_user_func_array([ $this, $callback ], $args);
571
	}
572
}
573