Completed
Branch master (c78441)
by Antarès
07:43 queued 02:23
created

AutoMethodsTrait::getMethodCallInfo()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.0042

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 31
ccs 17
cts 18
cp 0.9444
rs 8.439
cc 5
eloc 18
nc 5
nop 1
crap 5.0042
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 26
    public function __call($name, array $args)
33
    {
34 26
        $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 26
        $methodCallInfo = $this->getMethodCallInfo($name);
37 24
        $method = $methodCallInfo['method'];
38 24
        $property = $methodCallInfo['property'];
39 24
        $collectionProperties = $methodCallInfo['collectionProperties'];
40
41 24
        $valuesToUpdate = array();
42
43
        switch ($method) {
44 24
            case 'get':
45 24
            case 'is':
46 19
                return $this->$property;
47
48 21
            case 'set':
49
                // a setter should have exactly one argument
50 14
                MethodCallManager::assertArgsNumber(1, $args);
51
                // we set a collection here if there is an association with it
52
                if (
53 14
                    !empty($this->_collectionsItemNames['byProperty'][$property])
54 14
                    && !(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...
55 14
                ) {
56
                    $itemName = $this->_collectionsItemNames['byProperty'][$property]['itemName'];
1 ignored issue
show
Bug introduced by
The property _collectionsItemNames 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...
57
                    $propertyAddMethod = 'add' . strtoupper(substr($itemName, 0, 1)) . substr($itemName, 1);
58
                    $propertyRemoveMethod = 'remove' . strtoupper(substr($itemName, 0, 1)) . substr($itemName, 1);
59
60
                    foreach ($this->$property as $item) {
61
                        $this->$propertyRemoveMethod($item);
62
                    }
63
                    foreach ($args[0] as $item) {
64
                        $this->$propertyAddMethod($item);
65
                    }
66
                }
67
                // we set a regular property here
68
                else {
69 14
                    $oldValue = $this->$property;
70 14
                    $newValue = $args[0];
71
                    $valuesToUpdate = array(
72 14
                        'oldValue' => $oldValue,
73
                        'newValue' => $newValue
74 14
                    );
75
                    // check that the setter argument respects the property constraints
76 14
                    $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...
77
78 11
                    if ($oldValue !== $newValue) {
79 11
                        $this->$property = $newValue;
80 11
                    }
81
                }
82 11
                break;
83
84 10
            case 'add':
85 10
            case 'remove':
86 10
                $valueToUpdate = ($method === 'add') ? 'newValue' : 'oldValue';
87 10
                switch ($collectionProperties['behavior']) {
88 10
                    case 'list':
89 4
                        ListManager::$method($this->$property, $args);
90 4
                        $valuesToUpdate[$valueToUpdate] = $args[0];
91 4
                        break;
92 6
                    case 'map':
93 2
                        MapManager::$method($this->$property, $args);
94 1
                        break;
95 4
                    case 'set':
96 4
                        SetManager::$method($this->$property, $args);
97 4
                        $valuesToUpdate[$valueToUpdate] = $args[0];
98 4
                        break;
99 9
                }
100 9
                break;
101
        }
102
103
        // manage associations
104 17
        if (in_array($method, array('set', 'add', 'remove'))) {
105 17
            $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...
106 17
        }
107
108 17
        return $this;
109
    }
110
111
    /**
112
     * Extract the info about the method called.
113
     *
114
     * @param string $name
115
     *
116
     * @return array
117
     */
118 26
    private function getMethodCallInfo($name)
119
    {
120
        // check that the called method is a valid method name
121
        // also get the call type and the property to access
122 26
        $callIsValid = preg_match("/(set|get|is|add|remove)([A-Z].*)/", $name, $pregMatches);
123 26
        if (!$callIsValid) {
124
            throw new \BadMethodCallException("Method $name does not exist.");
125
        }
126
127 26
        $method = $pregMatches[1];
128 26
        $property = strtolower(substr($pregMatches[2], 0, 1)) . substr($pregMatches[2], 1);
129 26
        $collectionProperties = null;
130 26
        if (in_array($method, array('add', 'remove'))) {
131 10
            $collectionProperties = $this->_collectionsItemNames['byItemName'][$property];
132 10
            $property = $collectionProperties['property'];
133 10
        }
134
135
        // check that the method is accepted by the targeted property
136
        if (
137 26
            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...
138 24
            || !in_array($method, $this->_accessProperties[$property])
139 26
        ) {
140 2
            throw new \BadMethodCallException("Method $name does not exist.");
141
        }
142
143
        return array(
144 24
            'method' => $method,
145 24
            'property' => $property,
146
            'collectionProperties' => $collectionProperties
147 24
        );
148
    }
149
}
150