1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the Code-Insight library. |
4
|
|
|
* For the full copyright and license information, please view |
5
|
|
|
* the LICENSE file that was distributed with this source code. |
6
|
|
|
* |
7
|
|
|
* @copyright Alexander Obuhovich <[email protected]> |
8
|
|
|
* @link https://github.com/console-helpers/code-insight |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace ConsoleHelpers\CodeInsight\KnowledgeBase\DataCollector; |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
use Go\ParserReflection\ReflectionFileNamespace; |
15
|
|
|
|
16
|
|
|
class FunctionDataCollector extends AbstractDataCollector |
17
|
|
|
{ |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Collect data from a namespace. |
21
|
|
|
* |
22
|
|
|
* @param integer $file_id File id. |
23
|
|
|
* @param ReflectionFileNamespace $namespace Namespace. |
24
|
|
|
* |
25
|
|
|
* @return void |
26
|
|
|
*/ |
27
|
4 |
|
public function collectData($file_id, ReflectionFileNamespace $namespace) |
28
|
|
|
{ |
29
|
|
|
$sql = 'SELECT Name, Id |
30
|
|
|
FROM Functions |
31
|
4 |
|
WHERE FileId = :file_id'; |
32
|
4 |
|
$old_functions = $this->db->fetchPairs($sql, array( |
33
|
4 |
|
'file_id' => $file_id, |
34
|
4 |
|
)); |
35
|
|
|
|
36
|
|
|
$insert_sql = ' INSERT INTO Functions (FileId, Name, ParameterCount, RequiredParameterCount, IsVariadic, ReturnsReference, HasReturnType, ReturnType) |
37
|
4 |
|
VALUES (:file_id, :name, :parameter_count, :required_parameter_count, :is_variadic, :returns_reference, :has_return_type, :return_type)'; |
38
|
|
|
$update_sql = ' UPDATE Functions |
39
|
|
|
SET ParameterCount = :parameter_count, |
40
|
|
|
RequiredParameterCount = :required_parameter_count, |
41
|
|
|
IsVariadic = :is_variadic, |
42
|
|
|
ReturnsReference = :returns_reference, |
43
|
|
|
ReturnType = :return_type, |
44
|
|
|
HasReturnType = :has_return_type |
45
|
4 |
|
WHERE FileId = :file_id AND Name = :name'; |
46
|
|
|
|
47
|
4 |
|
$new_functions = array(); |
48
|
|
|
|
49
|
4 |
|
foreach ( $namespace->getFunctions() as $function ) { |
50
|
4 |
|
$function_name = $function->getName(); |
51
|
4 |
|
$new_functions[] = $function_name; |
52
|
|
|
|
53
|
4 |
|
$has_return_type = $function->hasReturnType(); |
54
|
4 |
|
$return_type = $has_return_type ? (string)$function->getReturnType() : null; |
55
|
|
|
|
56
|
4 |
|
$this->db->perform( |
57
|
4 |
|
isset($old_functions[$function_name]) ? $update_sql : $insert_sql, |
58
|
|
|
array( |
59
|
4 |
|
'file_id' => $file_id, |
60
|
4 |
|
'name' => $function_name, |
61
|
4 |
|
'parameter_count' => $function->getNumberOfParameters(), |
62
|
4 |
|
'required_parameter_count' => $function->getNumberOfRequiredParameters(), |
63
|
4 |
|
'is_variadic' => (int)$function->isVariadic(), |
64
|
4 |
|
'returns_reference' => (int)$function->returnsReference(), |
65
|
4 |
|
'has_return_type' => (int)$has_return_type, |
66
|
4 |
|
'return_type' => $return_type, |
67
|
|
|
) |
68
|
4 |
|
); |
69
|
|
|
|
70
|
4 |
|
$function_id = isset($old_functions[$function_name]) ? $old_functions[$function_name] : $this->db->lastInsertId(); |
71
|
4 |
|
$this->processFunctionParameters($function_id, $function); |
72
|
4 |
|
} |
73
|
|
|
|
74
|
4 |
|
$delete_functions = array_diff(array_keys($old_functions), $new_functions); |
75
|
|
|
|
76
|
4 |
|
if ( $delete_functions ) { |
77
|
|
|
$this->deleteFunctions($file_id, $delete_functions); |
78
|
|
|
} |
79
|
4 |
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Deletes functions. |
83
|
|
|
* |
84
|
|
|
* @param integer $file_id File ID. |
85
|
|
|
* @param array $functions Methods. |
86
|
|
|
* |
87
|
|
|
* @return void |
88
|
|
|
*/ |
89
|
1 |
|
protected function deleteFunctions($file_id, array $functions) |
90
|
|
|
{ |
91
|
1 |
|
if ( $functions ) { |
92
|
|
|
// Delete only given functions. |
93
|
|
|
$sql = 'SELECT Id |
94
|
|
|
FROM Functions |
95
|
|
|
WHERE FileId = :file_id AND Name IN (:names)'; |
96
|
|
|
$function_ids = $this->db->fetchCol($sql, array( |
97
|
|
|
'file_id' => $file_id, |
98
|
|
|
'names' => $functions, |
99
|
|
|
)); |
100
|
|
|
} |
101
|
|
|
else { |
102
|
|
|
// Delete all functions in a file. |
103
|
|
|
$sql = 'SELECT Id |
104
|
|
|
FROM Functions |
105
|
1 |
|
WHERE FileId = :file_id'; |
106
|
1 |
|
$function_ids = $this->db->fetchCol($sql, array( |
107
|
1 |
|
'file_id' => $file_id, |
108
|
1 |
|
)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// @codeCoverageIgnoreStart |
112
|
|
|
if ( !$function_ids ) { |
113
|
|
|
return; |
114
|
|
|
} |
115
|
|
|
// @codeCoverageIgnoreEnd |
116
|
|
|
|
117
|
1 |
|
$sql = 'DELETE FROM Functions WHERE Id IN (:function_ids)'; |
118
|
1 |
|
$this->db->perform($sql, array('function_ids' => $function_ids)); |
119
|
|
|
|
120
|
1 |
|
$sql = 'DELETE FROM FunctionParameters WHERE FunctionId IN (:function_ids)'; |
121
|
1 |
|
$this->db->perform($sql, array('function_ids' => $function_ids)); |
122
|
1 |
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Processes function parameters. |
126
|
|
|
* |
127
|
|
|
* @param integer $function_id Function ID. |
128
|
|
|
* @param \ReflectionFunction $function Function. |
129
|
|
|
* |
130
|
|
|
* @return void |
131
|
|
|
*/ |
132
|
4 |
|
protected function processFunctionParameters($function_id, \ReflectionFunction $function) |
133
|
|
|
{ |
134
|
|
|
$sql = 'SELECT Name |
135
|
|
|
FROM FunctionParameters |
136
|
4 |
|
WHERE FunctionId = :function_id'; |
137
|
4 |
|
$old_parameters = $this->db->fetchCol($sql, array( |
138
|
4 |
|
'function_id' => $function_id, |
139
|
4 |
|
)); |
140
|
|
|
|
141
|
|
|
$insert_sql = ' INSERT INTO FunctionParameters (FunctionId, Name, Position, TypeClass, HasType, TypeName, AllowsNull, IsArray, IsCallable, IsOptional, IsVariadic, CanBePassedByValue, IsPassedByReference, HasDefaultValue, DefaultValue, DefaultConstant) |
142
|
4 |
|
VALUES (:function_id, :name, :position, :type_class, :has_type, :type_name, :allows_null, :is_array, :is_callable, :is_optional, :is_variadic, :can_be_passed_by_value, :is_passed_by_reference, :has_default_value, :default_value, :default_constant)'; |
143
|
|
|
$update_sql = ' UPDATE FunctionParameters |
144
|
|
|
SET Position = :position, |
145
|
|
|
TypeClass = :type_class, |
146
|
|
|
HasType = :has_type, |
147
|
|
|
TypeName = :type_name, |
148
|
|
|
AllowsNull = :allows_null, |
149
|
|
|
IsArray = :is_array, |
150
|
|
|
IsCallable = :is_callable, |
151
|
|
|
IsOptional = :is_optional, |
152
|
|
|
IsVariadic = :is_variadic, |
153
|
|
|
CanBePassedByValue = :can_be_passed_by_value, |
154
|
|
|
IsPassedByReference = :is_passed_by_reference, |
155
|
|
|
HasDefaultValue = :has_default_value, |
156
|
|
|
DefaultValue = :default_value, |
157
|
|
|
DefaultConstant = :default_constant |
158
|
4 |
|
WHERE FunctionId = :function_id AND Name = :name'; |
159
|
|
|
|
160
|
4 |
|
$new_parameters = array(); |
161
|
|
|
|
162
|
4 |
|
foreach ( $function->getParameters() as $position => $parameter ) { |
163
|
4 |
|
$parameter_name = $parameter->getName(); |
164
|
4 |
|
$new_parameters[] = $parameter_name; |
165
|
|
|
|
166
|
4 |
|
$type_class = $parameter->getClass(); |
167
|
4 |
|
$type_class = $type_class ? $type_class->getName() : null; |
168
|
|
|
|
169
|
4 |
|
$has_type = $parameter->hasType(); |
|
|
|
|
170
|
4 |
|
$type_name = $has_type ? (string)$parameter->getType() : null; |
|
|
|
|
171
|
|
|
|
172
|
4 |
|
$has_default_value = $parameter->isDefaultValueAvailable(); |
173
|
4 |
|
$default_value_is_constant = $has_default_value ? $parameter->isDefaultValueConstant() : false; |
|
|
|
|
174
|
|
|
|
175
|
4 |
|
$this->db->perform( |
176
|
4 |
|
in_array($parameter_name, $old_parameters) ? $update_sql : $insert_sql, |
177
|
|
|
array( |
178
|
4 |
|
'function_id' => $function_id, |
179
|
4 |
|
'name' => $parameter_name, |
180
|
4 |
|
'position' => $position, |
181
|
4 |
|
'type_class' => $type_class, |
182
|
4 |
|
'has_type' => (int)$has_type, |
183
|
4 |
|
'type_name' => $type_name, |
184
|
4 |
|
'allows_null' => (int)$parameter->allowsNull(), |
185
|
4 |
|
'is_array' => (int)$parameter->isArray(), |
186
|
4 |
|
'is_callable' => (int)$parameter->isCallable(), |
187
|
4 |
|
'is_optional' => (int)$parameter->isOptional(), |
188
|
4 |
|
'is_variadic' => (int)$parameter->isVariadic(), |
|
|
|
|
189
|
4 |
|
'can_be_passed_by_value' => (int)$parameter->canBePassedByValue(), |
190
|
4 |
|
'is_passed_by_reference' => (int)$parameter->isPassedByReference(), |
191
|
4 |
|
'has_default_value' => (int)$has_default_value, |
192
|
4 |
|
'default_value' => $has_default_value ? json_encode($parameter->getDefaultValue()) : null, |
193
|
4 |
|
'default_constant' => $default_value_is_constant ? $parameter->getDefaultValueConstantName() : null, |
194
|
|
|
) |
195
|
4 |
|
); |
196
|
4 |
|
} |
197
|
|
|
|
198
|
4 |
|
$delete_parameters = array_diff($old_parameters, $new_parameters); |
199
|
|
|
|
200
|
4 |
|
if ( $delete_parameters ) { |
201
|
|
|
$sql = 'DELETE FROM FunctionParameters |
202
|
1 |
|
WHERE FunctionId = :function_id AND Name IN (:names)'; |
203
|
1 |
|
$this->db->perform($sql, array( |
204
|
1 |
|
'function_id' => $function_id, |
205
|
1 |
|
'names' => $delete_parameters, |
206
|
1 |
|
)); |
207
|
1 |
|
} |
208
|
4 |
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Delete previously collected data for a files. |
212
|
|
|
* |
213
|
|
|
* @param array $file_ids File IDs. |
214
|
|
|
* |
215
|
|
|
* @return void |
216
|
|
|
*/ |
217
|
1 |
|
public function deleteData(array $file_ids) |
218
|
|
|
{ |
219
|
1 |
|
foreach ( $file_ids as $file_id ) { |
220
|
1 |
|
$this->deleteFunctions($file_id, array()); |
221
|
1 |
|
} |
222
|
1 |
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Returns statistics about the code. |
226
|
|
|
* |
227
|
|
|
* @return array |
228
|
|
|
*/ |
229
|
1 |
|
public function getStatistics() |
230
|
|
|
{ |
231
|
|
|
$sql = 'SELECT COUNT(*) |
232
|
1 |
|
FROM Functions'; |
233
|
1 |
|
$function_count = $this->db->fetchValue($sql); |
234
|
|
|
|
235
|
|
|
return array( |
236
|
1 |
|
'Functions' => $function_count, |
237
|
1 |
|
); |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
} |
241
|
|
|
|
Let’s take a look at an example:
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
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: