Completed
Push — master ( 12bb39...81be87 )
by Antarès
03:15
created

AutoMethodsTrait   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 86.67%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 21
c 3
b 0
f 2
lcom 1
cbo 1
dl 0
loc 138
ccs 65
cts 75
cp 0.8667
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
C __call() 0 76 16
B getMethodCallInfo() 0 31 5
1
<?php
2
3
namespace Accessible;
4
5
use \Accessible\MethodManager\MethodCallManager;
6
use \Accessible\MethodManager\ListManager;
7
use \Accessible\MethodManager\MapManager;
8
use \Accessible\MethodManager\SetManager;
9
10
trait AutoMethodsTrait
11
{
12
    /**
13
     * This function will be called each time a getter or a setter that is not
14
     * already defined in the class is called.
15
     *
16
     * @param  string $name The name of the called function.
17
     *                      It must be a getter or a setter.
18
     * @param  array  $args The array of arguments for the called function.
19
     *                      It should be empty for a getter call,
20
     *                      and should have one item for a setter call.
21
     *
22
     * @return mixed    The value that should be returned by the function called if it is a getter,
23
     *                  the object itself if the function called is a setter.
24
     *
25
     * @throws \BadMethodCallException      When the method called is neither a getter nor a setter,
26
     *         						   		or if the access right has not be given for this method,
27
     *         						     	or if the method is a setter called without argument.
28
     * @throws \InvalidArgumentException    When the argument given to the method called (as a setter)
29
     *         								does not satisfy the constraints attached to the property
30
     *         								to modify.
31
     */
32 34
    public function __call($name, array $args)
33
    {
34 34
        $this->getPropertiesInfo();
0 ignored issues
show
Documentation Bug introduced by
The method getPropertiesInfo does not exist on object<Accessible\AutoMethodsTrait>? 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...
35
36 34
        $methodCallInfo = $this->getMethodCallInfo($name);
37 31
        $method = $methodCallInfo['method'];
38 31
        $property = $methodCallInfo['property'];
39 31
        $collectionProperties = $methodCallInfo['collectionProperties'];
40
41 31
        $valuesToUpdate = array();
42
43
        switch ($method) {
44 31
            case 'get':
45 31
            case 'is':
46 31
            case 'call':
47 25
                MethodCallManager::assertArgsNumber(0, $args);
48 24
                return $this->$property;
49
50 26
            case 'set':
51 18
                MethodCallManager::assertArgsNumber(1, $args);
52
                // we set a collection here if there is an association with it
53
                if (
54 17
                    !empty($this->_collectionsItemNames['byProperty'][$property])
55 17
                    && !(empty($this->_associationsList[$property]))
0 ignored issues
show
Bug introduced by
The property _associationsList 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...
56 17
                ) {
57
                    $itemName = $this->_collectionsItemNames['byProperty'][$property]['itemName'];
58
                    $propertyAddMethod = 'add' . strtoupper(substr($itemName, 0, 1)) . substr($itemName, 1);
59
                    $propertyRemoveMethod = 'remove' . strtoupper(substr($itemName, 0, 1)) . substr($itemName, 1);
60
61
                    foreach ($this->$property as $item) {
62
                        $this->$propertyRemoveMethod($item);
63
                    }
64
                    foreach ($args[0] as $item) {
65
                        $this->$propertyAddMethod($item);
66
                    }
67
                }
68
                // we set a regular property here
69
                else {
70 17
                    $oldValue = $this->$property;
71 17
                    $newValue = $args[0];
72
                    $valuesToUpdate = array(
73 17
                        'oldValue' => $oldValue,
74
                        'newValue' => $newValue
75 17
                    );
76
                    // check that the setter argument respects the property constraints
77 17
                    $this->assertPropertyValue($property, $newValue);
0 ignored issues
show
Documentation Bug introduced by
The method assertPropertyValue does not exist on object<Accessible\AutoMethodsTrait>? 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...
78 14
                    $this->$property = $newValue;
79
                }
80 14
                break;
81
82 12
            case 'add':
83 12
            case 'remove':
84 12
                $valueToUpdate = ($method === 'add') ? 'newValue' : 'oldValue';
85 12
                switch ($collectionProperties['behavior']) {
86 12
                    case 'list':
87 5
                        ListManager::$method($this->$property, $args);
88 5
                        $valuesToUpdate[$valueToUpdate] = $args[0];
89 5
                        break;
90 7
                    case 'map':
91 3
                        MapManager::$method($this->$property, $args);
92 2
                        break;
93 4
                    case 'set':
94 4
                        SetManager::$method($this->$property, $args);
95 4
                        $valuesToUpdate[$valueToUpdate] = $args[0];
96 4
                        break;
97 11
                }
98 11
                break;
99
        }
100
101
        // manage associations
102 21
        if (in_array($method, array('set', 'add', 'remove'))) {
103 21
            $this->updatePropertyAssociation($property, $valuesToUpdate);
0 ignored issues
show
Documentation Bug introduced by
The method updatePropertyAssociation does not exist on object<Accessible\AutoMethodsTrait>? 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...
104 21
        }
105
106 21
        return $this;
107
    }
108
109
    /**
110
     * Extract the info about the method called.
111
     *
112
     * @param string $name
113
     *
114
     * @return array
115
     */
116 34
    private function getMethodCallInfo($name)
117
    {
118 34
        $extractedMethod = preg_match("/^(set|get|is|add|remove)([A-Z].*)/", $name, $pregMatches);
119 34
        if ($extractedMethod) {
120 33
            $method = $pregMatches[1];
121 33
            $property = lcfirst($pregMatches[2]);
122 33
        } else {
123 2
            $method = 'call';
124 2
            $property = $name;
125
        }
126
127 34
        $collectionProperties = null;
128 34
        if (in_array($method, array('add', 'remove'))) {
129 12
            $collectionProperties = $this->_collectionsItemNames['byItemName'][$property];
130 12
            $property = $collectionProperties['property'];
131 12
        }
132
133
        // check that the method is accepted by the targeted property
134
        if (
135 34
            empty($this->_accessProperties[$property])
0 ignored issues
show
Bug introduced by
The property _accessProperties 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...
136 31
            || !in_array($method, $this->_accessProperties[$property])
137 34
        ) {
138 3
            throw new \BadMethodCallException("Method $name does not exist.");
139
        }
140
141
        return array(
142 31
            'method' => $method,
143 31
            'property' => $property,
144
            'collectionProperties' => $collectionProperties
145 31
        );
146
    }
147
}
148