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
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.