Lister   B
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 199
Duplicated Lines 1.51 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
dl 3
loc 199
rs 8.3999
c 0
b 0
f 0
wmc 38
lcom 1
cbo 12

6 Methods

Rating   Name   Duplication   Size   Complexity  
A processParent() 3 22 3
C processItems() 0 45 14
A getPaginationBlock() 0 10 2
B getContents() 0 24 6
C handleAjax() 0 30 7
B handle() 0 28 6

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * @package Cadmium\System\Modules\Entitizer
5
 * @author Anton Romanov
6
 * @copyright Copyright (c) 2015-2017, Anton Romanov
7
 * @link http://cadmium-cms.com
8
 */
9
10
namespace Modules\Entitizer\Utils {
11
12
	use Frames, Modules\Entitizer, Modules\Settings, Utils\Pagination, Utils\View, Ajax, Number, Request, Template, Url;
13
14
	abstract class Lister extends Frames\Admin\Area\Panel {
15
16
		protected $parent = null, $entity = null, $path = [], $depth = 0, $index = 0;
17
18
		protected $items = ['list' => [], 'total' => 0];
19
20
		/**
21
		 * Process the parent block
22
		 */
23
24
		private function processParent(Template\Block $parent) {
25
26
			# Set parent id
27
28
			$parent->id = $this->parent->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
Bug introduced by
The property id does not seem to exist in Modules\Entitizer\Utils\Entity.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
29
30
			# Set create button
31
32
			if (count($this->path) < CONFIG_ENTITIZER_MAX_DEPTH) $parent->getBlock('create')->id = $this->parent->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
33
34
			else { $parent->getBlock('create')->disable(); $parent->getBlock('create_disabled')->enable(); }
35
36
			# Set edit button
37
38 View Code Duplication
			if (0 !== $this->parent->id) $parent->getBlock('edit')->id = $this->parent->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Documentation introduced by
The property id does not exist on object<Modules\Entitizer\Utils\Entity>. 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...
39
40
			else { $parent->getBlock('edit')->disable(); $parent->getBlock('edit_disabled')->enable(); }
41
42
			# Add parent additional data
43
44
			$this->processEntityParent($parent);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Modules\Entitizer\Utils\Lister as the method processEntityParent() does only exist in the following sub-classes of Modules\Entitizer\Utils\Lister: Modules\Entitizer\Lister\Menuitems, Modules\Entitizer\Lister\Pages. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
45
		}
46
47
		/**
48
		 * Process the items block
49
		 */
50
51
		private function processItems(Template\Block $items, bool $ajax = false) {
52
53
			foreach ($this->items['list'] as $item) {
54
55
				if ((null !== $this->entity) && ($item['dataset']->id === $this->entity->id)) continue;
56
57
				$items->addItem($view = View::get(!$ajax ? static::$view_item : static::$view_ajax_item));
58
59
				# Set data
60
61
				$view->id = $item['dataset']->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
62
63
				$view->set(static::$naming, $item['dataset']->get(static::$naming));
64
65
				# Set buttons
66
67
				if (!$ajax) {
68
69
					if (static::$nesting) {
70
71
						$fertile = ((count($this->path) + 1) < CONFIG_ENTITIZER_MAX_DEPTH);
72
73
						$view->getBlock('create')->class = ($fertile ? 'olive' : 'disabled');
0 ignored issues
show
Documentation introduced by
The property class does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
74
75
						$view->getBlock('create')->id = $item['dataset']->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
76
					}
77
78
					$super = (static::$super && ($item['dataset']->id === 1));
79
80
					$has_children = (static::$nesting && ($item['children'] > 0));
81
82
					$view->getBlock('remove')->class = ((!$super && !$has_children) ? 'negative' : 'disabled');
0 ignored issues
show
Documentation introduced by
The property class does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
83
84
				} else {
85
86
					$selectable = ((count($this->path) + $this->depth + 1) < CONFIG_ENTITIZER_MAX_DEPTH);
87
88
					$view->getBlock('select')->class = ($selectable ? 'grey' : 'disabled');
0 ignored issues
show
Documentation introduced by
The property class does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
89
				}
90
91
				# Add item additional data
92
93
				$this->processItem($view, $item['dataset'], (static::$nesting ? $item['children'] : 0));
0 ignored issues
show
Bug introduced by
The method processItem() does not exist on Modules\Entitizer\Utils\Lister. Did you maybe mean processItems()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
94
			}
95
		}
96
97
		/**
98
		 * Get the pagination block
99
		 *
100
		 * @return Template\Block|false : a block or false if there is only a single page
101
		 */
102
103
		private function getPaginationBlock() {
104
105
			$query = (static::$nesting ? ('?parent_id=' . $this->parent->id) : '');
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Modules\Entitizer\Utils\Entity>. 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...
106
107
			$url = new Url(INSTALL_PATH . static::$link . $query);
108
109
			# ------------------------
110
111
			return Pagination::getBlock($this->index, Settings::get('admin_display_entities'), $this->items['total'], $url);
112
		}
113
114
		/**
115
		 * Get the contents block
116
		 */
117
118
		private function getContents(bool $ajax = false) : Template\Block {
119
120
			$contents = View::get(!$ajax ? static::$view_main : static::$view_ajax_main);
121
122
			# Set path
123
124
			if (static::$nesting) $contents->path = $this->path;
0 ignored issues
show
Documentation introduced by
The property path does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
125
126
			# Process parent block
127
128
			if (static::$nesting && !$ajax) $this->processParent($contents->getBlock('parent'));
129
130
			# Process items block
131
132
			$this->processItems($contents->getBlock('items'), $ajax);
133
134
			# Set pagination
135
136
			if (!$ajax) $contents->pagination = $this->getPaginationBlock();
0 ignored issues
show
Documentation introduced by
The property pagination does not exist on object<Template\Block>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
137
138
			# ------------------------
139
140
			return $contents;
141
		}
142
143
		/**
144
		 * Handle the ajax request
145
		 */
146
147
		private function handleAjax() : Ajax\Response {
148
149
			$ajax = Ajax::createResponse();
150
151
			# Create parent entity
152
153
			$parent_id = (static::$nesting ? Number::forceInt(Request::get('parent_id')) : 0);
154
155
			$this->parent = Entitizer::get(static::$table, $parent_id);
156
157
			# Create active entity
158
159
			$this->entity = Entitizer::get(static::$table, Number::forceInt(Request::post('id')));
160
161
			# Get path and depth
162
163
			if (false !== ($path = $this->parent->getPath())) $this->path = $path;
164
165
			if ((0 !== $this->entity->id) && (false !== ($depth = $this->entity->getSubtreeDepth()))) $this->depth = $depth;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Modules\Entitizer\Utils\Entity>. 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...
166
167
			# Get items list
168
169
			$lister = (static::$nesting ? 'getChildren' : 'getItems');
170
171
			if (false !== ($items = $this->parent->$lister())) $this->items = $items;
172
173
			# ------------------------
174
175
			return $ajax->set('contents', $this->getContents(true)->getContents());
176
		}
177
178
		/**
179
		 * Handle the request
180
		 *
181
		 * @return Template\Block|Ajax\Response : a block object if the ajax param was set to false, otherwise an ajax response
182
		 */
183
184
		protected function handle(bool $ajax = false) {
185
186
			if ($ajax) return $this->handleAjax();
187
188
			# Create parent entity
189
190
			$parent_id = (static::$nesting ? Number::forceInt(Request::get('parent_id')) : 0);
191
192
			$this->parent = Entitizer::get(static::$table, $parent_id);
193
194
			# Get path
195
196
			if (false !== ($path = $this->parent->getPath())) $this->path = $path;
197
198
			# Get index
199
200
			$this->index = Number::forceInt(Request::get('index'), 1, 999999);
201
202
			# Get items list
203
204
			$lister = (static::$nesting ? 'getChildren' : 'getItems'); $display = Settings::get('admin_display_entities');
205
206
			if (false !== ($items = $this->parent->$lister([], [], $this->index, $display))) $this->items = $items;
207
208
			# ------------------------
209
210
			return $this->getContents();
211
		}
212
	}
213
}
214