Passed
Pull Request — master (#10)
by Anton
07:57 queued 04:02
created

Handler::processParent()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 30
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
rs 8.439
cc 5
eloc 11
nc 9
nop 1
1
<?php
2
3
namespace Modules\Entitizer\Utils {
4
5
	use Modules\Entitizer, Utils\Popup, Utils\View, Ajax, Language, Number, Request, Template;
6
7
	abstract class Handler {
8
9
		protected $create = false, $entity = null, $parent = null, $path = [], $form = null;
10
11
		# Process parent block
12
13
		private function processParent(Template\Asset\Block $parent) {
14
15
			# Set parent id
16
17
			$parent->id = $this->parent->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<Template\Asset\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...
18
19
			# Set create button
20
21
			if (count($this->path) < CONFIG_ENTITIZER_MAX_DEPTH) {
22
23
				$parent->block('create')->class = ($this->create ? 'active item' : 'item');
24
25
				$parent->block('create')->id = $this->parent->id;
26
27
			} else { $parent->block('create')->disable(); $parent->block('create_disabled')->enable(); }
28
29
			# Set edit button
30
31
			if (0 !== $this->parent->id) {
32
33
				$parent->block('edit')->class = (!$this->create ? 'active item' : 'item');
34
35
				$parent->block('edit')->id = $this->parent->id;
36
37
			} else { $parent->block('edit')->disable(); $parent->block('edit_disabled')->enable(); }
38
39
			# Add parent additional data
40
41
			$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\Handler as the method processEntityParent() does only exist in the following sub-classes of Modules\Entitizer\Utils\Handler: Modules\Entitizer\Handler\Menuitem, Modules\Entitizer\Handler\Page. 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...
42
		}
43
44
		# Process selector block
45
46
		private function processSelector(Template\Asset\Block $selector) {
47
48
			if ($this->create) return $selector->disable();
49
50
			$selector->parent_id = $this->entity->parent_id;
0 ignored issues
show
Documentation introduced by
The property parent_id does not exist on object<Template\Asset\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...
51
52
			$parent = Entitizer::get(static::$table, $this->entity->parent_id);
53
54
			$selector->super_parent_id = $parent->parent_id;
0 ignored issues
show
Documentation introduced by
The property super_parent_id does not exist on object<Template\Asset\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...
55
56
			$selector->set(static::$naming, $parent->get(static::$naming));
57
		}
58
59
		# Get contents
60
61
		private function getContents() {
62
63
			$contents = View::get(static::$view);
64
65
			# Set id
66
67
			$contents->id = $this->entity->id;
68
69
			# Set path / title
70
71
			if (static::$nesting) $contents->path = $this->path;
72
73
			else $contents->title = ($this->create ? Language::get(static::$naming_new) : $this->entity->get(static::$naming));
74
75
			# Process parent block
76
77
			if (static::$nesting) $this->processParent($contents->block('parent'));
78
79
			# Set link
80
81
			$link = (INSTALL_PATH . static::$link . '/');
82
83
			if (static::$nesting) $contents->link = ($link . ($this->create ? 'create' : 'edit') . '?id=' . $this->parent->id);
84
85
			else $contents->link = ($link . ($this->create ? 'create' : ('edit?id=' . $this->entity->id)));
86
87
			# Process selector block
88
89
			if (static::$nesting) $this->processSelector($contents->block('selector'));
90
91
			# Implement form
92
93
			$this->form->implement($contents);
94
95
			# Add additional data for specific entity
96
97
			$this->processEntity($contents);
98
99
			# ------------------------
100
101
			return $contents;
102
		}
103
104
		# Handle ajax request
105
106
		private function handleAjax() {
107
108
			$ajax = Ajax::response();
109
110
			# Create entity
111
112
			$id = Number::format(Request::get('id'));
113
114
			$this->entity = Entitizer::get(static::$table, (!$this->create ? $id : 0));
115
116
			# Process actions
117
118
			if (Request::post('action') === 'move') {
119
120
				$parent_id = Number::format(Request::post('parent_id'));
121
122
				if (!$this->entity->move($parent_id)) return $ajax->error(Language::get(static::$message_error_move));
123
124 View Code Duplication
			} else if (Request::post('action') === 'remove') {
0 ignored issues
show
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...
125
126
				if (!$this->entity->remove()) return $ajax->error(Language::get(static::$message_error_remove));
127
			}
128
129
			# ------------------------
130
131
			return $ajax;
132
		}
133
134
		# Handle request
135
136
		public function handle(bool $create = false) {
137
138
			if (!($this->create = $create) && Request::isAjax()) return $this->handleAjax();
139
140
			# Create entity
141
142
			$id = Number::format(Request::get('id'));
143
144
			$this->entity = Entitizer::get(static::$table, (!$this->create ? $id : 0));
145
146
			if (!$this->create && (0 === $this->entity->id)) return Request::redirect(INSTALL_PATH . static::$link);
147
148
			# Create parent entity
149
150
			$this->parent = Entitizer::get(static::$table, (static::$nesting ? $id : 0));
151
152
			# Get path
153
154
			if (false !== ($path = $this->parent->path())) $this->path = $path;
155
156
			# Create form
157
158
			$this->form = new static::$form_class($this->entity);
159
160
			# Handle form
161
162
			if ($this->form->handle(new static::$controller($this->entity), true)) {
163
164
				if ($this->create && (0 !== $this->parent->id)) $this->entity->move($this->parent->id);
165
166
				Request::redirect(INSTALL_PATH . static::$link . '/edit?id=' . $this->entity->id . '&submitted');
167
			}
168
169
			# Display success message
170
171
			if (!$this->create && (false !== Request::get('submitted'))) {
172
173
				Popup::set('positive', Language::get(static::$message_success_save));
174
			}
175
176
			# ------------------------
177
178
			return $this->getContents();
179
		}
180
	}
181
}
182