1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Ducatel\PHPCollection\Base; |
4
|
|
|
|
5
|
|
|
abstract class AbstractTypedCollection extends AbstractCollection |
6
|
|
|
{ |
7
|
|
|
/** |
8
|
|
|
* @var \Closure The function used to check if an object is belongs to the type of this collection |
9
|
|
|
* This function must take one argument and return true when args is in valid type (false otherwise) |
10
|
|
|
*/ |
11
|
|
|
private $validateTypeFct; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @var \Closure The function used to check if two object are equals |
15
|
|
|
* This function must take two arguments and return true when arguments are equals (false otherwise) |
16
|
|
|
*/ |
17
|
|
|
private $equalsFct; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* AbstractTypedCollection constructor. |
21
|
|
|
* |
22
|
|
|
* @param string|\Closure $type Two value are possible for this field. |
23
|
|
|
* 1. The class name of object you want to store in this collection |
24
|
|
|
* 2. A function which take one arguments and return true when it in the good type (false otherwise) |
25
|
|
|
* @param null|\Closure $equalsFct When you pass null, the object will use the function \Equatable::equals if exist, else use the === |
26
|
|
|
* When you pass a Closure, the function must take two arguments and return true when 2 arguments are equals |
27
|
|
|
* @throws \TypeError When parameter are not in a valid type |
28
|
|
|
*/ |
29
|
14 |
|
public function __construct($type, $equalsFct = null) |
30
|
|
|
{ |
31
|
14 |
|
if ($this->is_closure($type) === false && is_string($type) === false) { |
32
|
1 |
|
throw new \TypeError('Parameter $type can be an object (string or instance) or a function'); |
|
|
|
|
33
|
|
|
} |
34
|
|
|
|
35
|
13 |
|
if ($equalsFct !== null && $this->is_closure($equalsFct) === false) { |
36
|
1 |
|
throw new \TypeError('Parameter $equalsFct can be null or a function'); |
|
|
|
|
37
|
|
|
} |
38
|
|
|
|
39
|
12 |
|
if (is_string($type)) { // Collection of object |
40
|
|
|
|
41
|
|
|
$this->validateTypeFct = function ($object) use ($type) { |
42
|
2 |
|
return ($object instanceof $type); |
43
|
|
|
}; |
44
|
|
|
|
45
|
2 |
|
if ($equalsFct === null) { |
46
|
2 |
|
$arrayOfInterface = class_implements($type); |
47
|
2 |
|
if (in_array("Ducatel\\PHPCollection\\Base\\Equatable", $arrayOfInterface)) { |
48
|
|
|
$this->equalsFct = function ($obj1, $obj2) { |
49
|
|
|
return $obj1->equals($obj2); |
50
|
1 |
|
}; |
51
|
|
|
} else { |
52
|
|
|
$this->equalsFct = function ($obj1, $obj2) { |
53
|
|
|
return $obj1 === $obj2; |
54
|
2 |
|
}; |
55
|
|
|
} |
56
|
|
|
} else { |
57
|
2 |
|
$this->equalsFct = $equalsFct; |
58
|
|
|
} |
59
|
|
|
} else { |
60
|
10 |
|
$this->validateTypeFct = $type; |
61
|
|
|
|
62
|
10 |
|
if ($equalsFct === null) { |
63
|
5 |
|
$this->equalsFct = function ($obj1, $obj2) { |
64
|
5 |
|
return $obj1 === $obj2; |
65
|
5 |
|
}; |
66
|
|
|
} else { |
67
|
5 |
|
$this->equalsFct = $equalsFct; |
68
|
|
|
} |
69
|
|
|
} |
70
|
12 |
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Check if an object can be manage by the current collection |
74
|
|
|
* @param $object The object you want to test |
75
|
|
|
* @return bool True if can be added false otherwise |
76
|
|
|
*/ |
77
|
12 |
|
public function validateType($object) : bool |
78
|
|
|
{ |
79
|
12 |
|
return (call_user_func($this->validateTypeFct, $object) === true); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Check if two object are equals |
84
|
|
|
* @param $object1 First object you want to compare to |
85
|
|
|
* @param $object2 Second object you want to compare |
86
|
|
|
* @return bool True if objects are equals false otherwise |
87
|
|
|
*/ |
88
|
10 |
|
private function checkEquality($object1, $object2) : bool |
89
|
|
|
{ |
90
|
10 |
|
return (call_user_func($this->equalsFct, $object1, $object2) === true); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Check if the object in param is a Closure |
95
|
|
|
* @param $object The object you want to test |
96
|
|
|
* @return bool True if it's a valid closure, else false |
97
|
|
|
*/ |
98
|
14 |
|
protected function is_closure($object) : bool |
99
|
|
|
{ |
100
|
14 |
|
return is_object($object) && ($object instanceof \Closure); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Check if an object is present in the collection. |
105
|
|
|
* The check will be done by `Equatable::equals()` method if exist else use `===` |
106
|
|
|
* |
107
|
|
|
* @param $objectToFind The object you want to check if it's already present. |
108
|
|
|
* |
109
|
|
|
* @return bool True if present else false |
110
|
|
|
* @throws \TypeError When object in parameter is not good |
111
|
|
|
*/ |
112
|
10 |
|
public function contains($objectToFind) : bool |
113
|
|
|
{ |
114
|
10 |
|
if ($this->validateType($objectToFind) === false) { |
115
|
5 |
|
throw new \TypeError("Object in parameter is not an instance of a good class"); |
|
|
|
|
116
|
|
|
} |
117
|
|
|
|
118
|
10 |
|
foreach ($this as $elem) { |
119
|
10 |
|
if ($this->checkEquality($elem, $objectToFind)) { |
120
|
10 |
|
return true; |
121
|
|
|
} |
122
|
|
|
} |
123
|
8 |
|
return false; |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.