1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik;
use Exception;
use Piwik\Container\StaticContainer;
use Psr\Log\LoggerInterface;
/**
* Development related checks and tools. You can enable/disable development using `./console development:enable` and
* `./console development:disable`. The intention of the development mode and this class is to support the developer
* as much as possible by doing some additional checks if the development mode is enabled. For instance if a developer
* has to register any class/method we can make sure whether they actually exist and if not display a useful error
* message. This helps the user to find for instance simple typos and makes sure it will actually work even if he
* forgets to test it.
*/
class Development
{
private static $isEnabled = null;
/**
* Returns `true` if development mode is enabled and `false` otherwise.
*
* @return bool
*/
public static function isEnabled()
{
if (is_null(self::$isEnabled)) {
self::$isEnabled = (bool) Config::getInstance()->Development['enabled'];
}
return self::$isEnabled;
}
/**
* Verifies whether a className of object implements the given method. It does not check whether the given method
* is actually callable (public).
*
* @param string|object $classOrObject
* @param string $method
*
* @return bool true if the method exists, false otherwise.
*/
public static function methodExists($classOrObject, $method)
{
if (is_string($classOrObject)) {
return class_exists($classOrObject) && method_exists($classOrObject, $method);
}
return method_exists($classOrObject, $method);
}
/**
* Formats a method call depending on the given class/object and method name. It does not perform any checks whether
* does actually exists.
*
* @param string|object $classOrObject
* @param string $method
*
* @return string Formatted method call. Example: "MyNamespace\MyClassname::methodName()"
*/
public static function formatMethodCall($classOrObject, $method)
{
if (is_object($classOrObject)) {
$classOrObject = get_class($classOrObject);
}
return $classOrObject . '::' . $method . '()';
}
/**
* Checks whether the given method is actually callable on the given class/object if the development mode is
* enabled. En error will be triggered if the method does not exist or is not callable (public) containing a useful
* error message for the developer.
*
* @param string|object $classOrObject
* @param string $method
* @param string $prefixMessageIfError You can prepend any string to the error message in case the method is not
* callable.
*/
public static function checkMethodIsCallable($classOrObject, $method, $prefixMessageIfError)
{
if (!self::isEnabled()) {
return;
}
self::checkMethodExists($classOrObject, $method, $prefixMessageIfError);
if (!self::isCallableMethod($classOrObject, $method)) {
self::error($prefixMessageIfError . ' "' . self::formatMethodCall($classOrObject, $method) . '" is not callable. Please make sure to method is public');
}
}
/**
* Checks whether the given method is actually callable on the given class/object if the development mode is
* enabled. En error will be triggered if the method does not exist or is not callable (public) containing a useful
* error message for the developer.
*
* @param string|object $classOrObject
* @param string $method
* @param string $prefixMessageIfError You can prepend any string to the error message in case the method is not
* callable.
*/
public static function checkMethodExists($classOrObject, $method, $prefixMessageIfError)
{
if (!self::isEnabled()) {
return;
}
if (!self::methodExists($classOrObject, $method)) {
self::error($prefixMessageIfError . ' "' . self::formatMethodCall($classOrObject, $method) . '" does not exist. Please make sure to define such a method.');
}
}
/**
* Verify whether the given method actually exists and is callable (public).
*
* @param string|object $classOrObject
* @param string $method
* @return bool
*/
public static function isCallableMethod($classOrObject, $method)
{
if (!self::methodExists($classOrObject, $method)) {
return false;
}
$reflection = new \ReflectionMethod($classOrObject, $method);
return $reflection->isPublic();
}
/**
* Triggers an error if the development mode is enabled. Depending on the current environment / mode it will either
* log the given message or throw an exception to make sure it will be displayed in the Piwik UI.
*
* @param string $message
* @throws Exception
*/
public static function error($message)
{
if (!self::isEnabled()) {
return;
}
$message .= ' (This error is only shown in development mode)';
if (SettingsServer::isTrackerApiRequest()
|| Common::isPhpCliMode()
) {
StaticContainer::get(LoggerInterface::class)->error($message, [
'ignoreInScreenWriter' => true,
]);
} else {
throw new Exception($message);
}
}
public static function getMethodSourceCode($className, $methodName)
{
$method = new \ReflectionMethod($className, $methodName);
$file = new \SplFileObject($method->getFileName());
$offset = $method->getStartLine() - 1;
$count = $method->getEndLine() - $method->getStartLine() + 1;
$fileIterator = new \LimitIterator($file, $offset, $count);
$methodCode = "\n " . $method->getDocComment() . "\n";
foreach ($fileIterator as $line) {
$methodCode .= $line;
}
$methodCode .= "\n";
return $methodCode;
}
public static function getUseStatements($className)
{
$class = new \ReflectionClass($className);
$file = new \SplFileObject($class->getFileName());
$fileIterator = new \LimitIterator($file, 0, $class->getStartLine());
$uses = array();
foreach ($fileIterator as $line) {
if (preg_match('/(\s*)use (.+)/', $line, $match)) {
$uses[] = trim($match[2]);
}
}
return $uses;
}
}
|