PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.5.1
Defender Security – Malware Scanner, Login Security & Firewall v5.5.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / framework / base / class-component.php

class-component.php in Defender Security – Malware Scanner, Login Security & Firewall 5.5.1, at framework/base/class-component.php

309 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base component class.
4 *
5 * @package Calotes\Base
6 */
7
8 namespace Calotes\Base;
9
10 use Exception;
11 use ReflectionClass;
12 use ReflectionMethod;
13 use ReflectionProperty;
14 use Calotes\Component\Behavior;
15
16 /**
17 * Base class for all components.
18 */
19 class Component {
20 /**
21 * Contains array of behaviors.
22 *
23 * @var array
24 */
25 protected $behaviors = array();
26
27 /**
28 * Cache the annotations of properties.
29 *
30 * @var array
31 */
32 public $annotations = array();
33
34 /**
35 * Store internal logging, mostly for debug.
36 *
37 * @var array
38 */
39 protected $internal_logging = array();
40
41 /**
42 * Attach a behavior to current class, a behavior is a mixins, which useful in case of pro/free version.
43 *
44 * @param string $name The name of the behavior.
45 * @param Behavior|string $behavior The behavior to attach.
46 */
47 public function attach_behavior( string $name, $behavior ): void {
48 // Make a fast init.
49 if ( ! $behavior instanceof Behavior ) {
50 $behavior = new $behavior();
51 }
52 $behavior->owner = $this;
53 $this->behaviors[ $name ] = $behavior;
54 }
55
56 /**
57 * Check if the object has a specific property.
58 *
59 * @param mixed $property The name of the property to check.
60 *
61 * @return bool
62 */
63 public function has_property( $property ): bool {
64 $ref = new ReflectionClass( $this );
65
66 return $ref->hasProperty( $property );
67 }
68
69 /**
70 * Check if the object has a specific method.
71 *
72 * @param mixed $method The name of the method to check.
73 *
74 * @return bool
75 */
76 public function has_method( $method ): bool {
77 $ref_class = new ReflectionClass( $this );
78 if ( $ref_class->hasMethod( $method ) ) {
79 return true;
80 }
81
82 foreach ( $this->behaviors as $behavior ) {
83 $ref_class = new ReflectionClass( $behavior );
84 if ( $ref_class->hasMethod( $method ) ) {
85 return true;
86 }
87 }
88
89 return false;
90 }
91
92 /**
93 * Get the value of a property.
94 *
95 * @param mixed $name The name of the property to get.
96 *
97 * @return mixed The value of the property.
98 * @throws Exception If the property is not found.
99 */
100 public function __get( $name ) {
101 // Priority to current class properties.
102 if ( $this->has_property( $name ) ) {
103 return $this->$name;
104 }
105 // Check if behaviors already have.
106 foreach ( $this->behaviors as $behavior ) {
107 $ref_class = new ReflectionClass( $behavior );
108
109 if ( $ref_class->hasProperty( $name ) ) {
110 return $ref_class->getProperty( $name )->getValue( $behavior );
111 }
112 }
113
114 throw new Exception( sprintf( 'Getting unknown property: %s::%s', esc_attr( get_class( $this ) ), esc_attr( $name ) ) );
115 }
116
117 /**
118 * Handles dynamic method calls on the object.
119 *
120 * @param mixed $name The name of the method to call.
121 * @param mixed $arguments The arguments to pass to the method.
122 *
123 * @return mixed The result of the method call.
124 * @throws Exception If the method is not found.
125 */
126 public function __call( $name, $arguments ) {
127 $ref_class = new ReflectionClass( $this );
128 if ( $ref_class->hasMethod( $name ) ) {
129 $ref_method = new ReflectionMethod( $this, $name );
130
131 return $ref_method->invokeArgs( $this, $arguments );
132 }
133 foreach ( $this->behaviors as $behavior ) {
134 $ref_class = new ReflectionClass( $behavior );
135 if ( $ref_class->hasMethod( $name ) ) {
136 $ref_method = new ReflectionMethod( $behavior, $name );
137
138 return $ref_method->invokeArgs( $behavior, $arguments );
139 }
140 }
141
142 throw new Exception( sprintf( 'Getting unknown property: %s::%s', esc_attr( get_class( $this ) ), esc_attr( $name ) ) );
143 }
144
145 /**
146 * Sets the value of a property.
147 *
148 * Do not call this directly, magic method for assign value to property.
149 * If property is not exist for this component, we will check its behavior.
150 *
151 * @param mixed $name The name of the property to set.
152 * @param mixed $value The value to set.
153 *
154 * @throws Exception If the property is not found.
155 */
156 public function __set( $name, $value ) {
157 $ref_class = new ReflectionClass( $this );
158 if ( $ref_class->hasProperty( $name ) ) {
159 $ref_class->getProperty( $name )->setValue( $value );
160
161 return;
162 }
163
164 foreach ( $this->behaviors as $behavior ) {
165 $ref_class = new ReflectionClass( $behavior );
166 if ( $ref_class->hasProperty( $name ) ) {
167 $ref_class->getProperty( $name )->setValue( $behavior, $value );
168
169 return;
170 }
171 }
172
173 throw new Exception( sprintf( 'Setting unknown property: %s::%s', esc_attr( get_class( $this ) ), esc_attr( $name ) ) );
174 }
175
176 /**
177 * It iterates over the properties of the class and checks if the property has a docblock containing the
178 * "@defender_property" tag. If the property has the tag, it extracts the type, sanitize, and rule information from
179 * the docblock and stores it in the "annotations" array.
180 *
181 * The list should be
182 * - type: for casting,
183 * - sanitize_*: the list of sanitize_ functions, which should be run on this property,
184 * - rule: the rule that we use for validation.
185 */
186 protected function parse_annotations() {
187 $class = new ReflectionClass( static::class );
188 $properties = $class->getProperties( ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED );
189 foreach ( $properties as $property ) {
190 $doc_block = $property->getDocComment();
191 if ( ! stristr( $doc_block, '@defender_property' ) ) {
192 continue;
193 }
194 $this->annotations[ $property->getName() ] = array(
195 'type' => $this->parse_annotations_var( $doc_block ),
196 'sanitize' => $this->parse_annotation_sanitize( $doc_block ),
197 'rule' => $this->parse_annotation_rule( $doc_block ),
198 );
199 }
200 }
201
202 /**
203 * Parses the variable type from a docblock.
204 *
205 * @param mixed $docblock The docblock to parse.
206 *
207 * @return string|bool The variable type if found, false otherwise.
208 */
209 private function parse_annotations_var( $docblock ) {
210 $pattern = '/@var\s(.+)/';
211 if ( preg_match( $pattern, $docblock, $matches ) ) {
212 $type = trim( $matches[1] );
213
214 // Only allow right type.
215 if ( in_array(
216 $type,
217 array( 'boolean', 'bool', 'integer', 'int', 'float', 'double', 'string', 'array', 'object' ),
218 true
219 ) ) {
220 return $type;
221 }
222 }
223
224 return false;
225 }
226
227 /**
228 * Get the sanitize function.
229 *
230 * @param mixed $docblock The docblock to parse.
231 *
232 * @return bool|string
233 */
234 private function parse_annotation_sanitize( $docblock ) {
235 $pattern = '/@(sanitize_.+)/';
236 if ( preg_match( $pattern, $docblock, $matches ) ) {
237 return trim( $matches[1] );
238 }
239
240 return false;
241 }
242
243 /**
244 * Get the validation rule.
245 *
246 * @param mixed $docblock The docblock to parse.
247 *
248 * @return bool|string
249 */
250 private function parse_annotation_rule( $docblock ) {
251 $pattern = '/@(rule_.+)/';
252 if ( preg_match( $pattern, $docblock, $matches ) ) {
253 return trim( $matches[1] );
254 }
255
256 return false;
257 }
258
259 /**
260 * Logs a message with an optional category.
261 *
262 * @param mixed $message The message to log. If it is not a string, array, or object, it will be converted to a
263 * string using print_r().
264 * @param string $category Optional. The category of the log. If provided, the log will be saved to a file with the
265 * category as the filename. If not provided, the log will not be saved to a file.
266 *
267 * @return void
268 */
269 protected function log( $message, $category = '' ): void {
270 global $wp_filesystem;
271 // Initialize the WP filesystem, no more using 'file-put-contents' function.
272 if ( empty( $wp_filesystem ) ) {
273 require_once ABSPATH . '/wp-admin/includes/file.php';
274 WP_Filesystem();
275 }
276 if ( ! is_string( $message ) || is_array( $message ) || is_object( $message ) ) {
277 $message = wp_json_encode( $message, JSON_PRETTY_PRINT );
278 }
279
280 $this->internal_logging[] = wp_date( 'Y-m-d H:i:s' ) . ' ' . $message;
281 /**
282 * Uncomment it for detailed logging on wp cli.
283 * if ( 'cli' === PHP_SAPI ) {
284 * echo $message . PHP_EOL;
285 * }
286 */
287 $message = '[' . wp_date( 'c' ) . '] ' . $message . PHP_EOL;
288
289 if ( $this->has_method( 'get_log_path' ) ) {
290 if ( ! empty( $category ) && 0 === preg_match( '/\.log$/', $category ) ) {
291 $category .= '.log';
292 }
293
294 $file_path = $this->get_log_path( $category );
295 $dir_name = pathinfo( $file_path, PATHINFO_DIRNAME );
296
297 if ( $wp_filesystem->exists( $file_path ) ) {
298 $message = $wp_filesystem->get_contents( $file_path ) . $message;
299 } elseif ( ! is_dir( $dir_name ) ) {
300 wp_mkdir_p( $dir_name );
301 }
302
303 if ( $wp_filesystem->is_writable( $dir_name ) ) {
304 $wp_filesystem->put_contents( $file_path, $message );
305 }
306 }
307 }
308 }
309