PluginProbe
Code Snippets / 2.14.0
Code Snippets v2.14.0
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / php / class-validator.php

class-validator.php in Code Snippets 2.14.0, at php/class-validator.php

225 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Validates code prior to execution.
5 *
6 * @package Code_Snippets
7 */
8 class Code_Snippets_Validator {
9
10 /**
11 * @var string
12 */
13 private $code;
14
15 /**
16 * @var array
17 */
18 private $tokens;
19
20 /**
21 * The index of the token currently being examined.
22 *
23 * @var int
24 */
25 private $current;
26
27 /**
28 * The total number of tokens.
29 *
30 * @var int
31 */
32 private $length;
33
34 /**
35 * Array to keep track of the various function, class and interface identifiers which have been defined.
36 *
37 * @var array
38 */
39 private $defined_identifiers = array();
40
41 /**
42 * Class constructor.
43 *
44 * @param string $code Snippet code for parsing.
45 */
46 public function __construct( $code ) {
47 $this->code = $code;
48 $this->tokens = token_get_all( "<?php\n" . $this->code );
49 $this->length = count( $this->tokens );
50 $this->current = 0;
51 }
52
53 /**
54 * Determine whether the parser has reached the end of the list of tokens.
55 *
56 * @return bool
57 */
58 private function end() {
59 return $this->current === $this->length;
60 }
61
62 /**
63 * Retrieve the next token without moving the pointer
64 *
65 * @return string|array|null The current token if the list has not been expended, null otherwise.
66 */
67 private function peek() {
68 return $this->end() ? null : $this->tokens[ $this->current ];
69 }
70
71 /**
72 * Move the pointer to the next token, if there is one
73 *
74 * If the first argument is provided, only move the pointer if the tokens match
75 *
76 * @return bool Whether the pointer was advanced.
77 */
78 private function next() {
79 if ( $this->end() ) {
80 return false;
81 }
82
83 $this->current++;
84 return true;
85 }
86
87 /**
88 * Check whether a particular identifier has been used previously.
89 *
90 * @param string $type Which type of identifier this is. Supports T_FUNCTION, T_CLASS and T_INTERFACE.
91 * @param string $identifier The name of the identifier itself.
92 *
93 * @return bool true if the identifier is not unique.
94 */
95 private function check_duplicate_identifier( $type, $identifier ) {
96
97 if ( ! isset( $this->defined_identifiers[ $type ] ) ) {
98 switch ( $type ) {
99 case T_FUNCTION:
100 $defined_functions = get_defined_functions();
101 $this->defined_identifiers[ T_FUNCTION ] = array_merge( $defined_functions['internal'], $defined_functions['user'] );
102 break;
103
104 case T_CLASS:
105 $this->defined_identifiers[ T_CLASS ] = get_declared_classes();
106 break;
107
108 case T_INTERFACE:
109 $this->defined_identifiers[ T_INTERFACE ] = get_declared_interfaces();
110 break;
111
112 default:
113 return false;
114 }
115 }
116
117 $duplicate = in_array( $identifier, $this->defined_identifiers[ $type ], true );
118 array_unshift( $this->defined_identifiers[ $type ], $identifier );
119 return $duplicate;
120 }
121
122 /**
123 * Validate the given PHP code and return the result.
124 * @return array|bool Array containing message if an error was encountered, false if validation was successful.
125 */
126 public function validate() {
127
128 while ( ! $this->end() ) {
129 $token = $this->peek();
130 $this->next();
131
132 if ( ! is_array( $token ) ) {
133 continue;
134 }
135
136 // only look for class and function declaration tokens
137 if ( T_CLASS !== $token[0] && T_FUNCTION !== $token[0] ) {
138 continue;
139 }
140
141 $structure_type = $token[0];
142
143 // continue eating tokens until we find the name of the class or function
144 while ( ! $this->end() && T_STRING !== $token[0] && ( T_FUNCTION !== $structure_type || '(' !== $token ) ) {
145 $token = $this->peek();
146 $this->next();
147 }
148
149 // if we've eaten all of the tokens without discovering a name, then there must be a syntax error, so return appropriately
150 if ( $this->end() ) {
151 return array(
152 'message' => __( 'Parse error: syntax error, unexpected end of snippet.', 'code-snippets' ),
153 'line' => $token[2],
154 );
155 }
156
157 // if the function is anonymous, with no name, then no need to check
158 if ( T_FUNCTION === $structure_type && '(' === $token ) {
159 continue;
160 }
161
162 // check whether the name has already been defined
163 if ( $this->check_duplicate_identifier( $structure_type, $token[1] ) ) {
164 switch ( $structure_type ) {
165 case T_FUNCTION:
166 /* translators: %s: PHP function name */
167 $message = __( 'Cannot redeclare function %s.', 'code-snippets' );
168 break;
169 case T_CLASS:
170 /* translators: %s: PHP class name */
171 $message = __( 'Cannot redeclare class %s.', 'code-snippets' );
172 break;
173 case T_INTERFACE:
174 /* translators: %s: PHP interface name */
175 $message = __( 'Cannot redeclare interface %s.', 'code-snippets' );
176 break;
177 default:
178 /* translators: %s: PHP identifier name*/
179 $message = __( 'Cannot redeclare %s.', 'code-snippets' );
180 }
181
182 return array(
183 'message' => sprintf( $message, $token[1] ),
184 'line' => $token[2],
185 );
186 }
187
188 // if we have entered into a class, eat tokens until we find the closing brace
189 if ( T_CLASS !== $structure_type ) {
190 continue;
191 }
192
193 // find the opening brace for the class
194 while ( ! $this->end() && '{' !== $token ) {
195 $token = $this->peek();
196 $this->next();
197 }
198
199 // continue traversing the class tokens until we have found the class closing brace
200 $depth = 1;
201 while ( ! $this->end() && $depth > 0 ) {
202 $token = $this->peek();
203
204 if ( '{' === $token ) {
205 $depth++;
206 } elseif ( '}' === $token ) {
207 $depth--;
208 }
209
210 $this->next();
211 }
212
213 // if we did not make it out of the class, then there's a problem
214 if ( $depth > 0 ) {
215 return array(
216 'message' => __( 'Parse error: syntax error, unexpected end of snippet', 'code-snippets' ),
217 'line' => $token[2],
218 );
219 }
220 }
221
222 return false;
223 }
224 }
225