Version Description
- Added support for wp-cli
=
Download this release
Release Info
Developer | johnbillion |
Plugin | WP Crontrol |
Version | 1.2 |
Comparing to | |
See all releases |
Code changes from version 1.1 to 1.2
- JSON.php +0 -806
- class-wp-cli.php +98 -0
- readme.txt +46 -19
- screenshot-1.png +0 -0
- screenshot-2.png +0 -0
- wp-crontrol.php +132 -101
JSON.php
DELETED
@@ -1,806 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
3 |
-
|
4 |
-
/**
|
5 |
-
* Converts to and from JSON format.
|
6 |
-
*
|
7 |
-
* JSON (JavaScript Object Notation) is a lightweight data-interchange
|
8 |
-
* format. It is easy for humans to read and write. It is easy for machines
|
9 |
-
* to parse and generate. It is based on a subset of the JavaScript
|
10 |
-
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
|
11 |
-
* This feature can also be found in Python. JSON is a text format that is
|
12 |
-
* completely language independent but uses conventions that are familiar
|
13 |
-
* to programmers of the C-family of languages, including C, C++, C#, Java,
|
14 |
-
* JavaScript, Perl, TCL, and many others. These properties make JSON an
|
15 |
-
* ideal data-interchange language.
|
16 |
-
*
|
17 |
-
* This package provides a simple encoder and decoder for JSON notation. It
|
18 |
-
* is intended for use with client-side Javascript applications that make
|
19 |
-
* use of HTTPRequest to perform server communication functions - data can
|
20 |
-
* be encoded into JSON notation for use in a client-side javascript, or
|
21 |
-
* decoded from incoming Javascript requests. JSON format is native to
|
22 |
-
* Javascript, and can be directly eval()'ed with no further parsing
|
23 |
-
* overhead
|
24 |
-
*
|
25 |
-
* All strings should be in ASCII or UTF-8 format!
|
26 |
-
*
|
27 |
-
* LICENSE: Redistribution and use in source and binary forms, with or
|
28 |
-
* without modification, are permitted provided that the following
|
29 |
-
* conditions are met: Redistributions of source code must retain the
|
30 |
-
* above copyright notice, this list of conditions and the following
|
31 |
-
* disclaimer. Redistributions in binary form must reproduce the above
|
32 |
-
* copyright notice, this list of conditions and the following disclaimer
|
33 |
-
* in the documentation and/or other materials provided with the
|
34 |
-
* distribution.
|
35 |
-
*
|
36 |
-
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
37 |
-
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
38 |
-
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
39 |
-
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
40 |
-
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
41 |
-
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
42 |
-
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
43 |
-
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
44 |
-
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
45 |
-
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
46 |
-
* DAMAGE.
|
47 |
-
*
|
48 |
-
* @category
|
49 |
-
* @package Services_JSON
|
50 |
-
* @author Michal Migurski <mike-json@teczno.com>
|
51 |
-
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
|
52 |
-
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
|
53 |
-
* @copyright 2005 Michal Migurski
|
54 |
-
* @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
|
55 |
-
* @license http://www.opensource.org/licenses/bsd-license.php
|
56 |
-
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
|
57 |
-
*/
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Marker constant for Services_JSON::decode(), used to flag stack state
|
61 |
-
*/
|
62 |
-
define('SERVICES_JSON_SLICE', 1);
|
63 |
-
|
64 |
-
/**
|
65 |
-
* Marker constant for Services_JSON::decode(), used to flag stack state
|
66 |
-
*/
|
67 |
-
define('SERVICES_JSON_IN_STR', 2);
|
68 |
-
|
69 |
-
/**
|
70 |
-
* Marker constant for Services_JSON::decode(), used to flag stack state
|
71 |
-
*/
|
72 |
-
define('SERVICES_JSON_IN_ARR', 3);
|
73 |
-
|
74 |
-
/**
|
75 |
-
* Marker constant for Services_JSON::decode(), used to flag stack state
|
76 |
-
*/
|
77 |
-
define('SERVICES_JSON_IN_OBJ', 4);
|
78 |
-
|
79 |
-
/**
|
80 |
-
* Marker constant for Services_JSON::decode(), used to flag stack state
|
81 |
-
*/
|
82 |
-
define('SERVICES_JSON_IN_CMT', 5);
|
83 |
-
|
84 |
-
/**
|
85 |
-
* Behavior switch for Services_JSON::decode()
|
86 |
-
*/
|
87 |
-
define('SERVICES_JSON_LOOSE_TYPE', 16);
|
88 |
-
|
89 |
-
/**
|
90 |
-
* Behavior switch for Services_JSON::decode()
|
91 |
-
*/
|
92 |
-
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
|
93 |
-
|
94 |
-
/**
|
95 |
-
* Converts to and from JSON format.
|
96 |
-
*
|
97 |
-
* Brief example of use:
|
98 |
-
*
|
99 |
-
* <code>
|
100 |
-
* // create a new instance of Services_JSON
|
101 |
-
* $json = new Services_JSON();
|
102 |
-
*
|
103 |
-
* // convert a complexe value to JSON notation, and send it to the browser
|
104 |
-
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
|
105 |
-
* $output = $json->encode($value);
|
106 |
-
*
|
107 |
-
* print($output);
|
108 |
-
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
|
109 |
-
*
|
110 |
-
* // accept incoming POST data, assumed to be in JSON notation
|
111 |
-
* $input = file_get_contents('php://input', 1000000);
|
112 |
-
* $value = $json->decode($input);
|
113 |
-
* </code>
|
114 |
-
*/
|
115 |
-
class Services_JSON
|
116 |
-
{
|
117 |
-
/**
|
118 |
-
* constructs a new JSON instance
|
119 |
-
*
|
120 |
-
* @param int $use object behavior flags; combine with boolean-OR
|
121 |
-
*
|
122 |
-
* possible values:
|
123 |
-
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
|
124 |
-
* "{...}" syntax creates associative arrays
|
125 |
-
* instead of objects in decode().
|
126 |
-
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
|
127 |
-
* Values which can't be encoded (e.g. resources)
|
128 |
-
* appear as NULL instead of throwing errors.
|
129 |
-
* By default, a deeply-nested resource will
|
130 |
-
* bubble up with an error, so all return values
|
131 |
-
* from encode() should be checked with isError()
|
132 |
-
*/
|
133 |
-
function Services_JSON($use = 0)
|
134 |
-
{
|
135 |
-
$this->use = $use;
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* convert a string from one UTF-16 char to one UTF-8 char
|
140 |
-
*
|
141 |
-
* Normally should be handled by mb_convert_encoding, but
|
142 |
-
* provides a slower PHP-only method for installations
|
143 |
-
* that lack the multibye string extension.
|
144 |
-
*
|
145 |
-
* @param string $utf16 UTF-16 character
|
146 |
-
* @return string UTF-8 character
|
147 |
-
* @access private
|
148 |
-
*/
|
149 |
-
function utf162utf8($utf16)
|
150 |
-
{
|
151 |
-
// oh please oh please oh please oh please oh please
|
152 |
-
if(function_exists('mb_convert_encoding')) {
|
153 |
-
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
|
154 |
-
}
|
155 |
-
|
156 |
-
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
|
157 |
-
|
158 |
-
switch(true) {
|
159 |
-
case ((0x7F & $bytes) == $bytes):
|
160 |
-
// this case should never be reached, because we are in ASCII range
|
161 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
162 |
-
return chr(0x7F & $bytes);
|
163 |
-
|
164 |
-
case (0x07FF & $bytes) == $bytes:
|
165 |
-
// return a 2-byte UTF-8 character
|
166 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
167 |
-
return chr(0xC0 | (($bytes >> 6) & 0x1F))
|
168 |
-
. chr(0x80 | ($bytes & 0x3F));
|
169 |
-
|
170 |
-
case (0xFFFF & $bytes) == $bytes:
|
171 |
-
// return a 3-byte UTF-8 character
|
172 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
173 |
-
return chr(0xE0 | (($bytes >> 12) & 0x0F))
|
174 |
-
. chr(0x80 | (($bytes >> 6) & 0x3F))
|
175 |
-
. chr(0x80 | ($bytes & 0x3F));
|
176 |
-
}
|
177 |
-
|
178 |
-
// ignoring UTF-32 for now, sorry
|
179 |
-
return '';
|
180 |
-
}
|
181 |
-
|
182 |
-
/**
|
183 |
-
* convert a string from one UTF-8 char to one UTF-16 char
|
184 |
-
*
|
185 |
-
* Normally should be handled by mb_convert_encoding, but
|
186 |
-
* provides a slower PHP-only method for installations
|
187 |
-
* that lack the multibye string extension.
|
188 |
-
*
|
189 |
-
* @param string $utf8 UTF-8 character
|
190 |
-
* @return string UTF-16 character
|
191 |
-
* @access private
|
192 |
-
*/
|
193 |
-
function utf82utf16($utf8)
|
194 |
-
{
|
195 |
-
// oh please oh please oh please oh please oh please
|
196 |
-
if(function_exists('mb_convert_encoding')) {
|
197 |
-
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
|
198 |
-
}
|
199 |
-
|
200 |
-
switch(strlen($utf8)) {
|
201 |
-
case 1:
|
202 |
-
// this case should never be reached, because we are in ASCII range
|
203 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
204 |
-
return $utf8;
|
205 |
-
|
206 |
-
case 2:
|
207 |
-
// return a UTF-16 character from a 2-byte UTF-8 char
|
208 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
209 |
-
return chr(0x07 & (ord($utf8{0}) >> 2))
|
210 |
-
. chr((0xC0 & (ord($utf8{0}) << 6))
|
211 |
-
| (0x3F & ord($utf8{1})));
|
212 |
-
|
213 |
-
case 3:
|
214 |
-
// return a UTF-16 character from a 3-byte UTF-8 char
|
215 |
-
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
216 |
-
return chr((0xF0 & (ord($utf8{0}) << 4))
|
217 |
-
| (0x0F & (ord($utf8{1}) >> 2)))
|
218 |
-
. chr((0xC0 & (ord($utf8{1}) << 6))
|
219 |
-
| (0x7F & ord($utf8{2})));
|
220 |
-
}
|
221 |
-
|
222 |
-
// ignoring UTF-32 for now, sorry
|
223 |
-
return '';
|
224 |
-
}
|
225 |
-
|
226 |
-
/**
|
227 |
-
* encodes an arbitrary variable into JSON format
|
228 |
-
*
|
229 |
-
* @param mixed $var any number, boolean, string, array, or object to be encoded.
|
230 |
-
* see argument 1 to Services_JSON() above for array-parsing behavior.
|
231 |
-
* if var is a strng, note that encode() always expects it
|
232 |
-
* to be in ASCII or UTF-8 format!
|
233 |
-
*
|
234 |
-
* @return mixed JSON string representation of input var or an error if a problem occurs
|
235 |
-
* @access public
|
236 |
-
*/
|
237 |
-
function encode($var)
|
238 |
-
{
|
239 |
-
switch (gettype($var)) {
|
240 |
-
case 'boolean':
|
241 |
-
return $var ? 'true' : 'false';
|
242 |
-
|
243 |
-
case 'NULL':
|
244 |
-
return 'null';
|
245 |
-
|
246 |
-
case 'integer':
|
247 |
-
return (int) $var;
|
248 |
-
|
249 |
-
case 'double':
|
250 |
-
case 'float':
|
251 |
-
return (float) $var;
|
252 |
-
|
253 |
-
case 'string':
|
254 |
-
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
|
255 |
-
$ascii = '';
|
256 |
-
$strlen_var = strlen($var);
|
257 |
-
|
258 |
-
/*
|
259 |
-
* Iterate over every character in the string,
|
260 |
-
* escaping with a slash or encoding to UTF-8 where necessary
|
261 |
-
*/
|
262 |
-
for ($c = 0; $c < $strlen_var; ++$c) {
|
263 |
-
|
264 |
-
$ord_var_c = ord($var{$c});
|
265 |
-
|
266 |
-
switch (true) {
|
267 |
-
case $ord_var_c == 0x08:
|
268 |
-
$ascii .= '\b';
|
269 |
-
break;
|
270 |
-
case $ord_var_c == 0x09:
|
271 |
-
$ascii .= '\t';
|
272 |
-
break;
|
273 |
-
case $ord_var_c == 0x0A:
|
274 |
-
$ascii .= '\n';
|
275 |
-
break;
|
276 |
-
case $ord_var_c == 0x0C:
|
277 |
-
$ascii .= '\f';
|
278 |
-
break;
|
279 |
-
case $ord_var_c == 0x0D:
|
280 |
-
$ascii .= '\r';
|
281 |
-
break;
|
282 |
-
|
283 |
-
case $ord_var_c == 0x22:
|
284 |
-
case $ord_var_c == 0x2F:
|
285 |
-
case $ord_var_c == 0x5C:
|
286 |
-
// double quote, slash, slosh
|
287 |
-
$ascii .= '\\'.$var{$c};
|
288 |
-
break;
|
289 |
-
|
290 |
-
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
|
291 |
-
// characters U-00000000 - U-0000007F (same as ASCII)
|
292 |
-
$ascii .= $var{$c};
|
293 |
-
break;
|
294 |
-
|
295 |
-
case (($ord_var_c & 0xE0) == 0xC0):
|
296 |
-
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
297 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
298 |
-
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
|
299 |
-
$c += 1;
|
300 |
-
$utf16 = $this->utf82utf16($char);
|
301 |
-
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
302 |
-
break;
|
303 |
-
|
304 |
-
case (($ord_var_c & 0xF0) == 0xE0):
|
305 |
-
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
306 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
307 |
-
$char = pack('C*', $ord_var_c,
|
308 |
-
ord($var{$c + 1}),
|
309 |
-
ord($var{$c + 2}));
|
310 |
-
$c += 2;
|
311 |
-
$utf16 = $this->utf82utf16($char);
|
312 |
-
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
313 |
-
break;
|
314 |
-
|
315 |
-
case (($ord_var_c & 0xF8) == 0xF0):
|
316 |
-
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
317 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
318 |
-
$char = pack('C*', $ord_var_c,
|
319 |
-
ord($var{$c + 1}),
|
320 |
-
ord($var{$c + 2}),
|
321 |
-
ord($var{$c + 3}));
|
322 |
-
$c += 3;
|
323 |
-
$utf16 = $this->utf82utf16($char);
|
324 |
-
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
325 |
-
break;
|
326 |
-
|
327 |
-
case (($ord_var_c & 0xFC) == 0xF8):
|
328 |
-
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
329 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
330 |
-
$char = pack('C*', $ord_var_c,
|
331 |
-
ord($var{$c + 1}),
|
332 |
-
ord($var{$c + 2}),
|
333 |
-
ord($var{$c + 3}),
|
334 |
-
ord($var{$c + 4}));
|
335 |
-
$c += 4;
|
336 |
-
$utf16 = $this->utf82utf16($char);
|
337 |
-
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
338 |
-
break;
|
339 |
-
|
340 |
-
case (($ord_var_c & 0xFE) == 0xFC):
|
341 |
-
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
342 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
343 |
-
$char = pack('C*', $ord_var_c,
|
344 |
-
ord($var{$c + 1}),
|
345 |
-
ord($var{$c + 2}),
|
346 |
-
ord($var{$c + 3}),
|
347 |
-
ord($var{$c + 4}),
|
348 |
-
ord($var{$c + 5}));
|
349 |
-
$c += 5;
|
350 |
-
$utf16 = $this->utf82utf16($char);
|
351 |
-
$ascii .= sprintf('\u%04s', bin2hex($utf16));
|
352 |
-
break;
|
353 |
-
}
|
354 |
-
}
|
355 |
-
|
356 |
-
return '"'.$ascii.'"';
|
357 |
-
|
358 |
-
case 'array':
|
359 |
-
/*
|
360 |
-
* As per JSON spec if any array key is not an integer
|
361 |
-
* we must treat the the whole array as an object. We
|
362 |
-
* also try to catch a sparsely populated associative
|
363 |
-
* array with numeric keys here because some JS engines
|
364 |
-
* will create an array with empty indexes up to
|
365 |
-
* max_index which can cause memory issues and because
|
366 |
-
* the keys, which may be relevant, will be remapped
|
367 |
-
* otherwise.
|
368 |
-
*
|
369 |
-
* As per the ECMA and JSON specification an object may
|
370 |
-
* have any string as a property. Unfortunately due to
|
371 |
-
* a hole in the ECMA specification if the key is a
|
372 |
-
* ECMA reserved word or starts with a digit the
|
373 |
-
* parameter is only accessible using ECMAScript's
|
374 |
-
* bracket notation.
|
375 |
-
*/
|
376 |
-
|
377 |
-
// treat as a JSON object
|
378 |
-
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
|
379 |
-
$properties = array_map(array($this, 'name_value'),
|
380 |
-
array_keys($var),
|
381 |
-
array_values($var));
|
382 |
-
|
383 |
-
foreach($properties as $property) {
|
384 |
-
if(Services_JSON::isError($property)) {
|
385 |
-
return $property;
|
386 |
-
}
|
387 |
-
}
|
388 |
-
|
389 |
-
return '{' . join(',', $properties) . '}';
|
390 |
-
}
|
391 |
-
|
392 |
-
// treat it like a regular array
|
393 |
-
$elements = array_map(array($this, 'encode'), $var);
|
394 |
-
|
395 |
-
foreach($elements as $element) {
|
396 |
-
if(Services_JSON::isError($element)) {
|
397 |
-
return $element;
|
398 |
-
}
|
399 |
-
}
|
400 |
-
|
401 |
-
return '[' . join(',', $elements) . ']';
|
402 |
-
|
403 |
-
case 'object':
|
404 |
-
$vars = get_object_vars($var);
|
405 |
-
|
406 |
-
$properties = array_map(array($this, 'name_value'),
|
407 |
-
array_keys($vars),
|
408 |
-
array_values($vars));
|
409 |
-
|
410 |
-
foreach($properties as $property) {
|
411 |
-
if(Services_JSON::isError($property)) {
|
412 |
-
return $property;
|
413 |
-
}
|
414 |
-
}
|
415 |
-
|
416 |
-
return '{' . join(',', $properties) . '}';
|
417 |
-
|
418 |
-
default:
|
419 |
-
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
|
420 |
-
? 'null'
|
421 |
-
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
|
422 |
-
}
|
423 |
-
}
|
424 |
-
|
425 |
-
/**
|
426 |
-
* array-walking function for use in generating JSON-formatted name-value pairs
|
427 |
-
*
|
428 |
-
* @param string $name name of key to use
|
429 |
-
* @param mixed $value reference to an array element to be encoded
|
430 |
-
*
|
431 |
-
* @return string JSON-formatted name-value pair, like '"name":value'
|
432 |
-
* @access private
|
433 |
-
*/
|
434 |
-
function name_value($name, $value)
|
435 |
-
{
|
436 |
-
$encoded_value = $this->encode($value);
|
437 |
-
|
438 |
-
if(Services_JSON::isError($encoded_value)) {
|
439 |
-
return $encoded_value;
|
440 |
-
}
|
441 |
-
|
442 |
-
return $this->encode(strval($name)) . ':' . $encoded_value;
|
443 |
-
}
|
444 |
-
|
445 |
-
/**
|
446 |
-
* reduce a string by removing leading and trailing comments and whitespace
|
447 |
-
*
|
448 |
-
* @param $str string string value to strip of comments and whitespace
|
449 |
-
*
|
450 |
-
* @return string string value stripped of comments and whitespace
|
451 |
-
* @access private
|
452 |
-
*/
|
453 |
-
function reduce_string($str)
|
454 |
-
{
|
455 |
-
$str = preg_replace(array(
|
456 |
-
|
457 |
-
// eliminate single line comments in '// ...' form
|
458 |
-
'#^\s*//(.+)$#m',
|
459 |
-
|
460 |
-
// eliminate multi-line comments in '/* ... */' form, at start of string
|
461 |
-
'#^\s*/\*(.+)\*/#Us',
|
462 |
-
|
463 |
-
// eliminate multi-line comments in '/* ... */' form, at end of string
|
464 |
-
'#/\*(.+)\*/\s*$#Us'
|
465 |
-
|
466 |
-
), '', $str);
|
467 |
-
|
468 |
-
// eliminate extraneous space
|
469 |
-
return trim($str);
|
470 |
-
}
|
471 |
-
|
472 |
-
/**
|
473 |
-
* decodes a JSON string into appropriate variable
|
474 |
-
*
|
475 |
-
* @param string $str JSON-formatted string
|
476 |
-
*
|
477 |
-
* @return mixed number, boolean, string, array, or object
|
478 |
-
* corresponding to given JSON input string.
|
479 |
-
* See argument 1 to Services_JSON() above for object-output behavior.
|
480 |
-
* Note that decode() always returns strings
|
481 |
-
* in ASCII or UTF-8 format!
|
482 |
-
* @access public
|
483 |
-
*/
|
484 |
-
function decode($str)
|
485 |
-
{
|
486 |
-
$str = $this->reduce_string($str);
|
487 |
-
|
488 |
-
switch (strtolower($str)) {
|
489 |
-
case 'true':
|
490 |
-
return true;
|
491 |
-
|
492 |
-
case 'false':
|
493 |
-
return false;
|
494 |
-
|
495 |
-
case 'null':
|
496 |
-
return null;
|
497 |
-
|
498 |
-
default:
|
499 |
-
$m = array();
|
500 |
-
|
501 |
-
if (is_numeric($str)) {
|
502 |
-
// Lookie-loo, it's a number
|
503 |
-
|
504 |
-
// This would work on its own, but I'm trying to be
|
505 |
-
// good about returning integers where appropriate:
|
506 |
-
// return (float)$str;
|
507 |
-
|
508 |
-
// Return float or int, as appropriate
|
509 |
-
return ((float)$str == (integer)$str)
|
510 |
-
? (integer)$str
|
511 |
-
: (float)$str;
|
512 |
-
|
513 |
-
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
|
514 |
-
// STRINGS RETURNED IN UTF-8 FORMAT
|
515 |
-
$delim = substr($str, 0, 1);
|
516 |
-
$chrs = substr($str, 1, -1);
|
517 |
-
$utf8 = '';
|
518 |
-
$strlen_chrs = strlen($chrs);
|
519 |
-
|
520 |
-
for ($c = 0; $c < $strlen_chrs; ++$c) {
|
521 |
-
|
522 |
-
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
523 |
-
$ord_chrs_c = ord($chrs{$c});
|
524 |
-
|
525 |
-
switch (true) {
|
526 |
-
case $substr_chrs_c_2 == '\b':
|
527 |
-
$utf8 .= chr(0x08);
|
528 |
-
++$c;
|
529 |
-
break;
|
530 |
-
case $substr_chrs_c_2 == '\t':
|
531 |
-
$utf8 .= chr(0x09);
|
532 |
-
++$c;
|
533 |
-
break;
|
534 |
-
case $substr_chrs_c_2 == '\n':
|
535 |
-
$utf8 .= chr(0x0A);
|
536 |
-
++$c;
|
537 |
-
break;
|
538 |
-
case $substr_chrs_c_2 == '\f':
|
539 |
-
$utf8 .= chr(0x0C);
|
540 |
-
++$c;
|
541 |
-
break;
|
542 |
-
case $substr_chrs_c_2 == '\r':
|
543 |
-
$utf8 .= chr(0x0D);
|
544 |
-
++$c;
|
545 |
-
break;
|
546 |
-
|
547 |
-
case $substr_chrs_c_2 == '\\"':
|
548 |
-
case $substr_chrs_c_2 == '\\\'':
|
549 |
-
case $substr_chrs_c_2 == '\\\\':
|
550 |
-
case $substr_chrs_c_2 == '\\/':
|
551 |
-
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
|
552 |
-
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
|
553 |
-
$utf8 .= $chrs{++$c};
|
554 |
-
}
|
555 |
-
break;
|
556 |
-
|
557 |
-
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
|
558 |
-
// single, escaped unicode character
|
559 |
-
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
|
560 |
-
. chr(hexdec(substr($chrs, ($c + 4), 2)));
|
561 |
-
$utf8 .= $this->utf162utf8($utf16);
|
562 |
-
$c += 5;
|
563 |
-
break;
|
564 |
-
|
565 |
-
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
|
566 |
-
$utf8 .= $chrs{$c};
|
567 |
-
break;
|
568 |
-
|
569 |
-
case ($ord_chrs_c & 0xE0) == 0xC0:
|
570 |
-
// characters U-00000080 - U-000007FF, mask 110XXXXX
|
571 |
-
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
572 |
-
$utf8 .= substr($chrs, $c, 2);
|
573 |
-
++$c;
|
574 |
-
break;
|
575 |
-
|
576 |
-
case ($ord_chrs_c & 0xF0) == 0xE0:
|
577 |
-
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
|
578 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
579 |
-
$utf8 .= substr($chrs, $c, 3);
|
580 |
-
$c += 2;
|
581 |
-
break;
|
582 |
-
|
583 |
-
case ($ord_chrs_c & 0xF8) == 0xF0:
|
584 |
-
// characters U-00010000 - U-001FFFFF, mask 11110XXX
|
585 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
586 |
-
$utf8 .= substr($chrs, $c, 4);
|
587 |
-
$c += 3;
|
588 |
-
break;
|
589 |
-
|
590 |
-
case ($ord_chrs_c & 0xFC) == 0xF8:
|
591 |
-
// characters U-00200000 - U-03FFFFFF, mask 111110XX
|
592 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
593 |
-
$utf8 .= substr($chrs, $c, 5);
|
594 |
-
$c += 4;
|
595 |
-
break;
|
596 |
-
|
597 |
-
case ($ord_chrs_c & 0xFE) == 0xFC:
|
598 |
-
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
|
599 |
-
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
|
600 |
-
$utf8 .= substr($chrs, $c, 6);
|
601 |
-
$c += 5;
|
602 |
-
break;
|
603 |
-
|
604 |
-
}
|
605 |
-
|
606 |
-
}
|
607 |
-
|
608 |
-
return $utf8;
|
609 |
-
|
610 |
-
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
|
611 |
-
// array, or object notation
|
612 |
-
|
613 |
-
if ($str{0} == '[') {
|
614 |
-
$stk = array(SERVICES_JSON_IN_ARR);
|
615 |
-
$arr = array();
|
616 |
-
} else {
|
617 |
-
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
618 |
-
$stk = array(SERVICES_JSON_IN_OBJ);
|
619 |
-
$obj = array();
|
620 |
-
} else {
|
621 |
-
$stk = array(SERVICES_JSON_IN_OBJ);
|
622 |
-
$obj = new stdClass();
|
623 |
-
}
|
624 |
-
}
|
625 |
-
|
626 |
-
array_push($stk, array('what' => SERVICES_JSON_SLICE,
|
627 |
-
'where' => 0,
|
628 |
-
'delim' => false));
|
629 |
-
|
630 |
-
$chrs = substr($str, 1, -1);
|
631 |
-
$chrs = $this->reduce_string($chrs);
|
632 |
-
|
633 |
-
if ($chrs == '') {
|
634 |
-
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
635 |
-
return $arr;
|
636 |
-
|
637 |
-
} else {
|
638 |
-
return $obj;
|
639 |
-
|
640 |
-
}
|
641 |
-
}
|
642 |
-
|
643 |
-
//print("\nparsing {$chrs}\n");
|
644 |
-
|
645 |
-
$strlen_chrs = strlen($chrs);
|
646 |
-
|
647 |
-
for ($c = 0; $c <= $strlen_chrs; ++$c) {
|
648 |
-
|
649 |
-
$top = end($stk);
|
650 |
-
$substr_chrs_c_2 = substr($chrs, $c, 2);
|
651 |
-
|
652 |
-
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
|
653 |
-
// found a comma that is not inside a string, array, etc.,
|
654 |
-
// OR we've reached the end of the character list
|
655 |
-
$slice = substr($chrs, $top['where'], ($c - $top['where']));
|
656 |
-
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
|
657 |
-
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
658 |
-
|
659 |
-
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
660 |
-
// we are in an array, so just push an element onto the stack
|
661 |
-
array_push($arr, $this->decode($slice));
|
662 |
-
|
663 |
-
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
664 |
-
// we are in an object, so figure
|
665 |
-
// out the property name and set an
|
666 |
-
// element in an associative array,
|
667 |
-
// for now
|
668 |
-
$parts = array();
|
669 |
-
|
670 |
-
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
671 |
-
// "name":value pair
|
672 |
-
$key = $this->decode($parts[1]);
|
673 |
-
$val = $this->decode($parts[2]);
|
674 |
-
|
675 |
-
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
676 |
-
$obj[$key] = $val;
|
677 |
-
} else {
|
678 |
-
$obj->$key = $val;
|
679 |
-
}
|
680 |
-
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
|
681 |
-
// name:value pair, where name is unquoted
|
682 |
-
$key = $parts[1];
|
683 |
-
$val = $this->decode($parts[2]);
|
684 |
-
|
685 |
-
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
|
686 |
-
$obj[$key] = $val;
|
687 |
-
} else {
|
688 |
-
$obj->$key = $val;
|
689 |
-
}
|
690 |
-
}
|
691 |
-
|
692 |
-
}
|
693 |
-
|
694 |
-
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
|
695 |
-
// found a quote, and we are not inside a string
|
696 |
-
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
|
697 |
-
//print("Found start of string at {$c}\n");
|
698 |
-
|
699 |
-
} elseif (($chrs{$c} == $top['delim']) &&
|
700 |
-
($top['what'] == SERVICES_JSON_IN_STR) &&
|
701 |
-
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
|
702 |
-
// found a quote, we're in a string, and it's not escaped
|
703 |
-
// we know that it's not escaped becase there is _not_ an
|
704 |
-
// odd number of backslashes at the end of the string so far
|
705 |
-
array_pop($stk);
|
706 |
-
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
|
707 |
-
|
708 |
-
} elseif (($chrs{$c} == '[') &&
|
709 |
-
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
710 |
-
// found a left-bracket, and we are in an array, object, or slice
|
711 |
-
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
|
712 |
-
//print("Found start of array at {$c}\n");
|
713 |
-
|
714 |
-
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
|
715 |
-
// found a right-bracket, and we're in an array
|
716 |
-
array_pop($stk);
|
717 |
-
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
718 |
-
|
719 |
-
} elseif (($chrs{$c} == '{') &&
|
720 |
-
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
721 |
-
// found a left-brace, and we are in an array, object, or slice
|
722 |
-
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
|
723 |
-
//print("Found start of object at {$c}\n");
|
724 |
-
|
725 |
-
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
|
726 |
-
// found a right-brace, and we're in an object
|
727 |
-
array_pop($stk);
|
728 |
-
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
729 |
-
|
730 |
-
} elseif (($substr_chrs_c_2 == '/*') &&
|
731 |
-
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
|
732 |
-
// found a comment start, and we are in an array, object, or slice
|
733 |
-
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
|
734 |
-
$c++;
|
735 |
-
//print("Found start of comment at {$c}\n");
|
736 |
-
|
737 |
-
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
|
738 |
-
// found a comment end, and we're in one now
|
739 |
-
array_pop($stk);
|
740 |
-
$c++;
|
741 |
-
|
742 |
-
for ($i = $top['where']; $i <= $c; ++$i)
|
743 |
-
$chrs = substr_replace($chrs, ' ', $i, 1);
|
744 |
-
|
745 |
-
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
|
746 |
-
|
747 |
-
}
|
748 |
-
|
749 |
-
}
|
750 |
-
|
751 |
-
if (reset($stk) == SERVICES_JSON_IN_ARR) {
|
752 |
-
return $arr;
|
753 |
-
|
754 |
-
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
|
755 |
-
return $obj;
|
756 |
-
|
757 |
-
}
|
758 |
-
|
759 |
-
}
|
760 |
-
}
|
761 |
-
}
|
762 |
-
|
763 |
-
/**
|
764 |
-
* @todo Ultimately, this should just call PEAR::isError()
|
765 |
-
*/
|
766 |
-
function isError($data, $code = null)
|
767 |
-
{
|
768 |
-
if (class_exists('pear')) {
|
769 |
-
return PEAR::isError($data, $code);
|
770 |
-
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
|
771 |
-
is_subclass_of($data, 'services_json_error'))) {
|
772 |
-
return true;
|
773 |
-
}
|
774 |
-
|
775 |
-
return false;
|
776 |
-
}
|
777 |
-
}
|
778 |
-
|
779 |
-
if (class_exists('PEAR_Error')) {
|
780 |
-
|
781 |
-
class Services_JSON_Error extends PEAR_Error
|
782 |
-
{
|
783 |
-
function Services_JSON_Error($message = 'unknown error', $code = null,
|
784 |
-
$mode = null, $options = null, $userinfo = null)
|
785 |
-
{
|
786 |
-
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
|
787 |
-
}
|
788 |
-
}
|
789 |
-
|
790 |
-
} else {
|
791 |
-
|
792 |
-
/**
|
793 |
-
* @todo Ultimately, this class shall be descended from PEAR_Error
|
794 |
-
*/
|
795 |
-
class Services_JSON_Error
|
796 |
-
{
|
797 |
-
function Services_JSON_Error($message = 'unknown error', $code = null,
|
798 |
-
$mode = null, $options = null, $userinfo = null)
|
799 |
-
{
|
800 |
-
|
801 |
-
}
|
802 |
-
}
|
803 |
-
|
804 |
-
}
|
805 |
-
|
806 |
-
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class-wp-cli.php
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/*
|
4 |
+
|
5 |
+
@todo add, delete, run, etc
|
6 |
+
|
7 |
+
*/
|
8 |
+
|
9 |
+
class Crontrol_Command extends WP_CLI_Command {
|
10 |
+
|
11 |
+
public $crontrol = null;
|
12 |
+
|
13 |
+
public function __construct() {
|
14 |
+
|
15 |
+
$this->crontrol = Crontrol::init();
|
16 |
+
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* List scheduled cron events.
|
21 |
+
*
|
22 |
+
* @since 1.2
|
23 |
+
*
|
24 |
+
* @alias list
|
25 |
+
* @subcommand list-events
|
26 |
+
*/
|
27 |
+
public function list_events() {
|
28 |
+
|
29 |
+
$events = $this->crontrol->get_cron_events();
|
30 |
+
|
31 |
+
if ( is_wp_error( $events ) ) {
|
32 |
+
WP_CLI::line( WP_CLI::error_to_string( $events ) );
|
33 |
+
die();
|
34 |
+
}
|
35 |
+
|
36 |
+
$events = array_map( array( $this, '_map_event' ), $events );
|
37 |
+
|
38 |
+
$fields = array(
|
39 |
+
'hook',
|
40 |
+
'next_run',
|
41 |
+
'recurrence'
|
42 |
+
);
|
43 |
+
|
44 |
+
\WP_CLI\Utils\format_items( 'table', $events, $fields );
|
45 |
+
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* List available cron schedules.
|
50 |
+
*
|
51 |
+
* @since 1.2
|
52 |
+
*
|
53 |
+
* @subcommand list-schedules
|
54 |
+
*/
|
55 |
+
public function list_schedules() {
|
56 |
+
|
57 |
+
$schedules = $this->crontrol->get_schedules();
|
58 |
+
|
59 |
+
$schedules = array_map( array( $this, '_map_schedule' ), $schedules );
|
60 |
+
|
61 |
+
$fields = array(
|
62 |
+
'display',
|
63 |
+
'interval'
|
64 |
+
);
|
65 |
+
|
66 |
+
\WP_CLI\Utils\format_items( 'table', $schedules, $fields );
|
67 |
+
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Test the WP Cron spawning system and report back any errors.
|
72 |
+
*
|
73 |
+
* @since 1.2
|
74 |
+
*/
|
75 |
+
public function test() {
|
76 |
+
|
77 |
+
$status = $this->crontrol->test_cron_spawn( false );
|
78 |
+
|
79 |
+
if ( is_wp_error( $status ) )
|
80 |
+
WP_CLI::error( $status );
|
81 |
+
else
|
82 |
+
WP_CLI::success( __( 'WP-Cron working as expected.', 'control' ) );
|
83 |
+
|
84 |
+
}
|
85 |
+
|
86 |
+
protected function _map_event( $event ) {
|
87 |
+
$event->next_run = strftime("%Y/%m/%d %H:%M:%S", $event->time) . " (".$this->crontrol->time_since(time(), $event->time).")";
|
88 |
+
$event->recurrence = ($event->schedule ? $event->interval.' ('.$this->crontrol->interval($event->interval).')' : __('Non-repeating', 'crontrol'));
|
89 |
+
return $event;
|
90 |
+
}
|
91 |
+
|
92 |
+
protected function _map_schedule( $schedule ) {
|
93 |
+
return (object) $schedule;
|
94 |
+
}
|
95 |
+
|
96 |
+
}
|
97 |
+
|
98 |
+
WP_CLI::add_command( 'crontrol', 'Crontrol_Command' );
|
readme.txt
CHANGED
@@ -1,29 +1,47 @@
|
|
1 |
-
=== WP
|
2 |
Contributors: scompt, johnbillion
|
3 |
-
|
4 |
-
Tags: admin, cron, plugin, control
|
5 |
Requires at least: 3.0
|
6 |
-
Tested up to: 3.
|
7 |
-
Stable tag: 1.
|
8 |
|
9 |
-
WP
|
10 |
|
11 |
== Description ==
|
12 |
|
13 |
-
WP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
== Installation ==
|
16 |
|
17 |
-
|
18 |
-
|
19 |
-
1. Go to the
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
== Frequently Asked Questions ==
|
23 |
|
24 |
= What's the use of adding new cron schedules? =
|
25 |
|
26 |
-
Cron schedules are used by WordPress and WordPress plugins to allow you to schedule commands to be executed at regular intervals. Intervals must be provided by the WordPress core or a plugin in order to be used. An example of a plugin that uses these schedules is [WordPress Database Backup](http://www.ilfilosofo.com/blog/wp-db-backup/). Out of the box, only daily and hourly backups are supported. In order to do a weekly backup, a weekly cron schedule must be entered into WP
|
27 |
|
28 |
= How do I create a new PHP cron entry? =
|
29 |
|
@@ -31,7 +49,7 @@ In the Tools -> Crontrol admin panel, click on the "add new PHP entry" link unde
|
|
31 |
|
32 |
= How do I create a new regular cron entry? =
|
33 |
|
34 |
-
There are two steps to getting a functioning cron entry that executes regularly. The first step is telling WordPress about the hook. This is the part that WP
|
35 |
|
36 |
*Step One: Adding the hook*
|
37 |
|
@@ -46,16 +64,20 @@ This part takes place in PHP code (for example, in the `functions.php` file from
|
|
46 |
The next step is to write your function. Here's a simple example:
|
47 |
|
48 |
`function my_function() {
|
49 |
-
wp_mail('
|
50 |
}`
|
51 |
|
52 |
= Do I really need the entire `wp-crontrol` directory? =
|
53 |
|
54 |
-
|
|
|
|
|
55 |
|
56 |
-
|
|
|
|
|
57 |
|
58 |
-
|
59 |
|
60 |
== Screenshots ==
|
61 |
|
@@ -64,11 +86,15 @@ Email [me](mailto:scompt@scompt.com).
|
|
64 |
|
65 |
== Upgrade Notice ==
|
66 |
|
67 |
-
= 1.
|
68 |
-
*
|
69 |
|
70 |
== Changelog ==
|
71 |
|
|
|
|
|
|
|
|
|
72 |
= 1.1 =
|
73 |
* Bug fixes for running cron jobs and adding cron schedules
|
74 |
* Added a cron spawn test to check for errors when spawning cron
|
@@ -95,3 +121,4 @@ Email [me](mailto:scompt@scompt.com).
|
|
95 |
|
96 |
= 0.1 =
|
97 |
* Super basic, look at what's in WP-Cron functionality.
|
|
1 |
+
=== WP Crontrol ===
|
2 |
Contributors: scompt, johnbillion
|
3 |
+
Tags: admin, cron, plugin, control, wp-cron, crontrol, wp-cli
|
|
|
4 |
Requires at least: 3.0
|
5 |
+
Tested up to: 3.6
|
6 |
+
Stable tag: 1.2
|
7 |
|
8 |
+
WP Crontrol lets you view and control what's happening in the WP-Cron system.
|
9 |
|
10 |
== Description ==
|
11 |
|
12 |
+
WP Crontrol lets you view and control what's happening in the WP-Cron system. From the admin screen you can:
|
13 |
+
|
14 |
+
* View all cron entries along with their arguments, recurrence and when they are next due.
|
15 |
+
* Edit, delete, and immediately run any cron entries.
|
16 |
+
* Add new cron entries.
|
17 |
+
|
18 |
+
The admin screen will show you a warning message if your cron system doesn't appear to be working (for example if your server can't connect to itself to fire scheduled cron entries).
|
19 |
+
|
20 |
+
From the settings screen you can also add, edit and remove cron schedues.
|
21 |
+
|
22 |
+
Now supports [wp-cli](http://wp-cli.org/)!
|
23 |
|
24 |
== Installation ==
|
25 |
|
26 |
+
You can install this plugin directly from your WordPress dashboard:
|
27 |
+
|
28 |
+
1. Go to the *Plugins* menu and click *Add New*.
|
29 |
+
2. Search for *WP Crontrol*.
|
30 |
+
3. Click *Install Now* next to the *WP Crontrol* plugin.
|
31 |
+
4. Activate the plugin.
|
32 |
+
|
33 |
+
Alternatively, see the guide to [Manually Installing Plugins](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
|
34 |
+
|
35 |
+
= Usage =
|
36 |
+
|
37 |
+
1. Go to the Settings -> Crontrol menu to add some new cron schedules.
|
38 |
+
2. Go to the Tools -> Crontrol menu to see what cron entries are scheduled and to add some new ones.
|
39 |
|
40 |
== Frequently Asked Questions ==
|
41 |
|
42 |
= What's the use of adding new cron schedules? =
|
43 |
|
44 |
+
Cron schedules are used by WordPress and WordPress plugins to allow you to schedule commands to be executed at regular intervals. Intervals must be provided by the WordPress core or a plugin in order to be used. An example of a plugin that uses these schedules is [WordPress Database Backup](http://www.ilfilosofo.com/blog/wp-db-backup/). Out of the box, only daily and hourly backups are supported. In order to do a weekly backup, a weekly cron schedule must be entered into WP Crontrol first and then the backup plugin can take advantage of it as an interval.
|
45 |
|
46 |
= How do I create a new PHP cron entry? =
|
47 |
|
49 |
|
50 |
= How do I create a new regular cron entry? =
|
51 |
|
52 |
+
There are two steps to getting a functioning cron entry that executes regularly. The first step is telling WordPress about the hook. This is the part that WP Crontrol was created to provide. The second step is calling your function when your hook is executed. You've got to do that on your own, but I'll explain how below.
|
53 |
|
54 |
*Step One: Adding the hook*
|
55 |
|
64 |
The next step is to write your function. Here's a simple example:
|
65 |
|
66 |
`function my_function() {
|
67 |
+
wp_mail('hello@example.com', 'WP Crontrol', 'WP Crontrol rocks!');
|
68 |
}`
|
69 |
|
70 |
= Do I really need the entire `wp-crontrol` directory? =
|
71 |
|
72 |
+
No, you can get rid of the whole directory and just use `wp-crontrol.php` if you wish. If you want to use wp-cli then you'll need to include `class-wp-cli.php` too.
|
73 |
+
|
74 |
+
= Which wp-cli commands are available? =
|
75 |
|
76 |
+
* `wp crontrol list` Lists the scheduled events on your site.
|
77 |
+
* `wp crontrol test` Performs a WP-Cron spawning test to make sure WP-Cron can function as expected.
|
78 |
+
* `wp crontrol list-schedules` Lists the available WP-Cron schedules on your site.
|
79 |
|
80 |
+
Note that wp-cli support was only recently added. This will be improved over time. Feedback welcome!
|
81 |
|
82 |
== Screenshots ==
|
83 |
|
86 |
|
87 |
== Upgrade Notice ==
|
88 |
|
89 |
+
= 1.2 =
|
90 |
+
* Added support for [wp-cli](http://wp-cli.org/)
|
91 |
|
92 |
== Changelog ==
|
93 |
|
94 |
+
= 1.2 =
|
95 |
+
* Added support for [wp-cli](http://wp-cli.org/)
|
96 |
+
* Removed some PHP4 code that's no longer relevant
|
97 |
+
|
98 |
= 1.1 =
|
99 |
* Bug fixes for running cron jobs and adding cron schedules
|
100 |
* Added a cron spawn test to check for errors when spawning cron
|
121 |
|
122 |
= 0.1 =
|
123 |
* Super basic, look at what's in WP-Cron functionality.
|
124 |
+
|
screenshot-1.png
DELETED
Binary file
|
screenshot-2.png
DELETED
Binary file
|
wp-crontrol.php
CHANGED
@@ -1,21 +1,21 @@
|
|
1 |
<?php
|
2 |
/*
|
3 |
-
* Plugin Name: WP
|
4 |
-
* Plugin URI:
|
5 |
-
* Description: WP
|
6 |
-
* Author:
|
7 |
-
* Version:
|
8 |
* Text Domain: crontrol
|
9 |
* Domain Path: /gettext/
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
-
* WP
|
14 |
*
|
15 |
* LICENSE
|
16 |
-
* This file is part of WP
|
17 |
*
|
18 |
-
* WP
|
19 |
* modify it under the terms of the GNU General Public License
|
20 |
* as published by the Free Software Foundation; either version 2
|
21 |
* of the License, or (at your option) any later version.
|
@@ -29,55 +29,58 @@
|
|
29 |
* along with this program; if not, write to the Free Software
|
30 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
31 |
*
|
32 |
-
* @package WP
|
33 |
* @author Edward Dale <scompt@scompt.com> & John Blackbourn <john@johnblackbourn.com>
|
34 |
* @copyright Copyright 2012 Edward Dale & John Blackbourn
|
35 |
* @license http://www.gnu.org/licenses/gpl.txt GPL 2.0
|
36 |
-
* @link http://
|
37 |
* @since 0.2
|
38 |
*/
|
|
|
|
|
|
|
39 |
class Crontrol {
|
40 |
var $json;
|
41 |
|
42 |
/**
|
43 |
* Hook onto all of the actions and filters needed by the plugin.
|
44 |
*/
|
45 |
-
function
|
46 |
define( 'CRONTROL_CRON_JOB', 'crontrol_cron_job');
|
47 |
$this->json = new Crontrol_JSON();
|
48 |
if( function_exists('add_action') ) {
|
49 |
-
add_action('init', array(&$this, '
|
50 |
-
add_action('init', array(&$this, '
|
51 |
-
add_action('admin_menu', array(&$this, '
|
52 |
|
53 |
// Make sure the activation works from subdirectories as well as
|
54 |
// directly in the plugin directory.
|
55 |
$activate_action = str_replace(ABSPATH.PLUGINDIR.'/', 'activate_', __FILE__);
|
56 |
-
add_action($activate_action, array(&$this, '
|
57 |
|
58 |
-
add_filter('cron_schedules', array(&$this, '
|
59 |
-
add_action(CRONTROL_CRON_JOB, array(&$this, '
|
60 |
}
|
61 |
}
|
62 |
|
63 |
/**
|
64 |
* Evaluates the provided code using eval.
|
65 |
*/
|
66 |
-
function
|
67 |
eval($code);
|
68 |
}
|
69 |
-
|
70 |
/**
|
71 |
* Run using the 'init' action.
|
72 |
*/
|
73 |
-
function
|
74 |
load_plugin_textdomain( 'crontrol', false, dirname( plugin_basename( __FILE__ ) ) . '/gettext' );
|
75 |
}
|
76 |
-
|
77 |
/**
|
78 |
* Handles any POSTs made by the plugin. Run using the 'init' action.
|
79 |
*/
|
80 |
-
function
|
81 |
if( isset($_POST['new_cron']) ) {
|
82 |
if( !current_user_can('manage_options') ) die(__('You are not allowed to add new cron events.', 'crontrol'));
|
83 |
check_admin_referer("new-cron");
|
@@ -94,7 +97,7 @@ class Crontrol {
|
|
94 |
$hookname = CRONTROL_CRON_JOB;
|
95 |
$this->add_cron($in_next_run, $in_schedule, $hookname, $args);
|
96 |
wp_redirect("tools.php?page=crontrol_admin_manage_page&crontrol_message=5&crontrol_name={$in_hookname}");
|
97 |
-
|
98 |
} else if( isset($_POST['edit_cron']) ) {
|
99 |
if( !current_user_can('manage_options') ) die(__('You are not allowed to edit cron events.', 'crontrol'));
|
100 |
|
@@ -173,7 +176,7 @@ class Crontrol {
|
|
173 |
}
|
174 |
}
|
175 |
}
|
176 |
-
|
177 |
/**
|
178 |
* Executes a cron entry immediately.
|
179 |
*
|
@@ -194,7 +197,7 @@ class Crontrol {
|
|
194 |
}
|
195 |
return false;
|
196 |
}
|
197 |
-
|
198 |
/**
|
199 |
* Adds a new cron entry.
|
200 |
*
|
@@ -213,7 +216,7 @@ class Crontrol {
|
|
213 |
return wp_schedule_event( $next_run, $schedule, $hookname, $args ) === NULL;
|
214 |
}
|
215 |
}
|
216 |
-
|
217 |
/**
|
218 |
* Deletes a cron entry.
|
219 |
*
|
@@ -228,7 +231,7 @@ class Crontrol {
|
|
228 |
}
|
229 |
return false;
|
230 |
}
|
231 |
-
|
232 |
/**
|
233 |
* Adds a new custom cron schedule.
|
234 |
*
|
@@ -241,7 +244,7 @@ class Crontrol {
|
|
241 |
$old_scheds[$name] = array('interval'=>$interval, 'display'=>$display);
|
242 |
update_option('crontrol_schedules', $old_scheds);
|
243 |
}
|
244 |
-
|
245 |
/**
|
246 |
* Deletes a custom cron schedule.
|
247 |
*
|
@@ -252,13 +255,13 @@ class Crontrol {
|
|
252 |
unset($scheds[$name]);
|
253 |
update_option('crontrol_schedules', $scheds);
|
254 |
}
|
255 |
-
|
256 |
/**
|
257 |
* Sets up the plugin environment upon first activation.
|
258 |
-
*
|
259 |
* Run using the 'activate_' action.
|
260 |
*/
|
261 |
-
function
|
262 |
$extra_scheds = array('twicedaily'=>array('interval'=>43200, 'display'=>__('Twice Daily', 'crontrol')));
|
263 |
add_option('crontrol_schedules', $extra_scheds);
|
264 |
|
@@ -267,17 +270,17 @@ class Crontrol {
|
|
267 |
_set_cron_array(array());
|
268 |
}
|
269 |
}
|
270 |
-
|
271 |
/**
|
272 |
* Adds options & management pages to the admin menu.
|
273 |
*
|
274 |
* Run using the 'admin_menu' action.
|
275 |
*/
|
276 |
-
function
|
277 |
$page = add_options_page('Crontrol', 'Crontrol', 'manage_options', 'crontrol_admin_options_page', array(&$this, 'admin_options_page') );
|
278 |
$page = add_management_page('Crontrol', "Crontrol", 'manage_options', 'crontrol_admin_manage_page', array(&$this, 'admin_manage_page') );
|
279 |
}
|
280 |
-
|
281 |
/**
|
282 |
* Gives WordPress the plugin's set of cron schedules.
|
283 |
*
|
@@ -286,11 +289,11 @@ class Crontrol {
|
|
286 |
* @param array $scheds The current cron schedules. Usually an empty array.
|
287 |
* @return array The existing cron schedules along with the plugin's schedules.
|
288 |
*/
|
289 |
-
function
|
290 |
$new_scheds = get_option('crontrol_schedules',array());
|
291 |
return array_merge($new_scheds, $scheds);
|
292 |
}
|
293 |
-
|
294 |
/**
|
295 |
* Displays the options page for the plugin.
|
296 |
*/
|
@@ -298,7 +301,7 @@ class Crontrol {
|
|
298 |
$schedules = $this->get_schedules();
|
299 |
$custom_schedules = get_option('crontrol_schedules',array());
|
300 |
$custom_keys = array_keys($custom_schedules);
|
301 |
-
|
302 |
if( isset($_GET['crontrol_message']) ) {
|
303 |
$messages = array( '2' => __("Successfully deleted the cron schedule <b>%s</b>", 'crontrol'),
|
304 |
'3' => __("Successfully added the cron schedule <b>%s</b>", 'crontrol'),
|
@@ -308,12 +311,12 @@ class Crontrol {
|
|
308 |
|
309 |
echo "<div id=\"message\" class=\"updated fade\"><p>$msg</p></div>";
|
310 |
}
|
311 |
-
|
312 |
?>
|
313 |
<div class="wrap">
|
314 |
<?php screen_icon(); ?>
|
315 |
<h2><?php _e("Cron Schedules", "crontrol"); ?></h2>
|
316 |
-
<p><?php _e('Cron schedules are the time intervals that are available to WordPress and plugin developers to schedule events. You can only delete cron schedules that you have created with WP
|
317 |
<div id="ajax-response"></div>
|
318 |
<table class="widefat">
|
319 |
<thead>
|
@@ -321,7 +324,7 @@ class Crontrol {
|
|
321 |
<th><?php _e('Name', 'crontrol'); ?></th>
|
322 |
<th><?php _e('Interval', 'crontrol'); ?></th>
|
323 |
<th><?php _e('Display Name', 'crontrol'); ?></th>
|
324 |
-
<th
|
325 |
</tr>
|
326 |
</thead>
|
327 |
<tbody>
|
@@ -345,7 +348,7 @@ class Crontrol {
|
|
345 |
echo "</tr>";
|
346 |
$class = empty($class)?"alternate":"";
|
347 |
}
|
348 |
-
}
|
349 |
?>
|
350 |
</tbody>
|
351 |
</table>
|
@@ -407,14 +410,14 @@ class Crontrol {
|
|
407 |
* Gets the status of WP-Cron functionality on the site by performing a test spawn. Cached for one hour when all is well.
|
408 |
*
|
409 |
*/
|
410 |
-
function test_cron_spawn() {
|
411 |
|
412 |
if ( defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON )
|
413 |
return true;
|
414 |
|
415 |
$cached_status = get_transient( 'wp-cron-test-ok' );
|
416 |
|
417 |
-
if ( $cached_status )
|
418 |
return true;
|
419 |
|
420 |
$doing_wp_cron = sprintf( '%.22F', microtime( true ) );
|
@@ -522,6 +525,40 @@ class Crontrol {
|
|
522 |
<?php
|
523 |
}
|
524 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
/**
|
526 |
* Displays the manage page for the plugin.
|
527 |
*/
|
@@ -538,8 +575,7 @@ class Crontrol {
|
|
538 |
|
539 |
echo "<div id=\"message\" class=\"updated fade\"><p>$msg</p></div>";
|
540 |
}
|
541 |
-
$
|
542 |
-
$schedules = $this->get_schedules();
|
543 |
$doing_edit = (isset( $_GET['action']) && $_GET['action']=='edit-cron') ? $_GET['id'] : false ;
|
544 |
$this->show_cron_status();
|
545 |
?>
|
@@ -554,40 +590,40 @@ class Crontrol {
|
|
554 |
<th><?php _e('Arguments', 'crontrol'); ?></th>
|
555 |
<th><?php _e('Next Run', 'crontrol'); ?></th>
|
556 |
<th><?php _e('Recurrence', 'crontrol'); ?></th>
|
557 |
-
<th colspan="3"
|
558 |
</tr>
|
559 |
</thead>
|
560 |
<tbody>
|
561 |
<?php
|
562 |
-
if(
|
563 |
?>
|
564 |
-
<tr><td colspan="7"><?php
|
565 |
<?php
|
566 |
} else {
|
567 |
$class = "";
|
568 |
-
foreach( $
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
}
|
592 |
}
|
593 |
?>
|
@@ -601,7 +637,7 @@ class Crontrol {
|
|
601 |
$this->show_cron_form((isset($_GET['action']) and $_GET['action']=='new-php-cron'), false);
|
602 |
}
|
603 |
}
|
604 |
-
|
605 |
/**
|
606 |
* Pretty-prints the difference in two times.
|
607 |
*
|
@@ -610,10 +646,10 @@ class Crontrol {
|
|
610 |
* @return string The pretty time_since value
|
611 |
* @link http://binarybonsai.com/code/timesince.txt
|
612 |
*/
|
613 |
-
function time_since($older_date, $newer_date) {
|
614 |
return $this->interval( $newer_date - $older_date );
|
615 |
}
|
616 |
-
|
617 |
function interval( $since ) {
|
618 |
// array of time period chunks
|
619 |
$chunks = array(
|
@@ -626,7 +662,7 @@ class Crontrol {
|
|
626 |
array( 1 , _n_noop('%s second', '%s seconds', 'crontrol')),
|
627 |
);
|
628 |
|
629 |
-
|
630 |
if( $since <= 0 ) {
|
631 |
return __('now', 'crontrol');
|
632 |
}
|
@@ -667,36 +703,31 @@ class Crontrol {
|
|
667 |
|
668 |
return $output;
|
669 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
670 |
}
|
671 |
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
require_once('JSON.php');
|
676 |
-
|
677 |
-
class Crontrol_JSON {
|
678 |
-
var $json;
|
679 |
-
function Crontrol_JSON() {
|
680 |
-
$this->json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
|
681 |
-
}
|
682 |
-
function encode($in) {
|
683 |
-
return $this->json->encode($in);
|
684 |
-
}
|
685 |
-
function decode($in) {
|
686 |
-
return $this->json->decode($in);
|
687 |
-
}
|
688 |
}
|
689 |
-
|
690 |
-
|
691 |
-
function encode($in) {
|
692 |
-
return json_encode($in);
|
693 |
-
}
|
694 |
-
function decode($in) {
|
695 |
-
return json_decode($in, true);
|
696 |
-
}
|
697 |
}
|
698 |
}
|
699 |
|
|
|
|
|
|
|
700 |
// Get this show on the road
|
701 |
-
|
702 |
-
?>
|
1 |
<?php
|
2 |
/*
|
3 |
+
* Plugin Name: WP Crontrol
|
4 |
+
* Plugin URI: http://wordpress.org/plugins/wp-crontrol/
|
5 |
+
* Description: WP Crontrol lets you view and control what's happening in the WP-Cron system.
|
6 |
+
* Author: <a href="http://www.scompt.com/">Edward Dale</a> & <a href="http://lud.icro.us/">John Blackbourn</a>
|
7 |
+
* Version: 1.2
|
8 |
* Text Domain: crontrol
|
9 |
* Domain Path: /gettext/
|
10 |
*/
|
11 |
|
12 |
/**
|
13 |
+
* WP Crontrol lets you take control over what's happening in the WP-Cron system.
|
14 |
*
|
15 |
* LICENSE
|
16 |
+
* This file is part of WP Crontrol.
|
17 |
*
|
18 |
+
* WP Crontrol is free software; you can redistribute it and/or
|
19 |
* modify it under the terms of the GNU General Public License
|
20 |
* as published by the Free Software Foundation; either version 2
|
21 |
* of the License, or (at your option) any later version.
|
29 |
* along with this program; if not, write to the Free Software
|
30 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
31 |
*
|
32 |
+
* @package WP Crontrol
|
33 |
* @author Edward Dale <scompt@scompt.com> & John Blackbourn <john@johnblackbourn.com>
|
34 |
* @copyright Copyright 2012 Edward Dale & John Blackbourn
|
35 |
* @license http://www.gnu.org/licenses/gpl.txt GPL 2.0
|
36 |
+
* @link http://wordpress.org/plugins/wp-crontrol/
|
37 |
* @since 0.2
|
38 |
*/
|
39 |
+
|
40 |
+
defined( 'ABSPATH' ) or die();
|
41 |
+
|
42 |
class Crontrol {
|
43 |
var $json;
|
44 |
|
45 |
/**
|
46 |
* Hook onto all of the actions and filters needed by the plugin.
|
47 |
*/
|
48 |
+
protected function __construct() {
|
49 |
define( 'CRONTROL_CRON_JOB', 'crontrol_cron_job');
|
50 |
$this->json = new Crontrol_JSON();
|
51 |
if( function_exists('add_action') ) {
|
52 |
+
add_action('init', array(&$this, 'action_init'));
|
53 |
+
add_action('init', array(&$this, 'action_handle_posts'));
|
54 |
+
add_action('admin_menu', array(&$this, 'action_admin_menu'));
|
55 |
|
56 |
// Make sure the activation works from subdirectories as well as
|
57 |
// directly in the plugin directory.
|
58 |
$activate_action = str_replace(ABSPATH.PLUGINDIR.'/', 'activate_', __FILE__);
|
59 |
+
add_action($activate_action, array(&$this, 'action_activate'));
|
60 |
|
61 |
+
add_filter('cron_schedules', array(&$this, 'filter_cron_schedules'));
|
62 |
+
add_action(CRONTROL_CRON_JOB, array(&$this, 'action_php_cron_entry'));
|
63 |
}
|
64 |
}
|
65 |
|
66 |
/**
|
67 |
* Evaluates the provided code using eval.
|
68 |
*/
|
69 |
+
function action_php_cron_entry($code) {
|
70 |
eval($code);
|
71 |
}
|
72 |
+
|
73 |
/**
|
74 |
* Run using the 'init' action.
|
75 |
*/
|
76 |
+
function action_init() {
|
77 |
load_plugin_textdomain( 'crontrol', false, dirname( plugin_basename( __FILE__ ) ) . '/gettext' );
|
78 |
}
|
79 |
+
|
80 |
/**
|
81 |
* Handles any POSTs made by the plugin. Run using the 'init' action.
|
82 |
*/
|
83 |
+
function action_handle_posts() {
|
84 |
if( isset($_POST['new_cron']) ) {
|
85 |
if( !current_user_can('manage_options') ) die(__('You are not allowed to add new cron events.', 'crontrol'));
|
86 |
check_admin_referer("new-cron");
|
97 |
$hookname = CRONTROL_CRON_JOB;
|
98 |
$this->add_cron($in_next_run, $in_schedule, $hookname, $args);
|
99 |
wp_redirect("tools.php?page=crontrol_admin_manage_page&crontrol_message=5&crontrol_name={$in_hookname}");
|
100 |
+
|
101 |
} else if( isset($_POST['edit_cron']) ) {
|
102 |
if( !current_user_can('manage_options') ) die(__('You are not allowed to edit cron events.', 'crontrol'));
|
103 |
|
176 |
}
|
177 |
}
|
178 |
}
|
179 |
+
|
180 |
/**
|
181 |
* Executes a cron entry immediately.
|
182 |
*
|
197 |
}
|
198 |
return false;
|
199 |
}
|
200 |
+
|
201 |
/**
|
202 |
* Adds a new cron entry.
|
203 |
*
|
216 |
return wp_schedule_event( $next_run, $schedule, $hookname, $args ) === NULL;
|
217 |
}
|
218 |
}
|
219 |
+
|
220 |
/**
|
221 |
* Deletes a cron entry.
|
222 |
*
|
231 |
}
|
232 |
return false;
|
233 |
}
|
234 |
+
|
235 |
/**
|
236 |
* Adds a new custom cron schedule.
|
237 |
*
|
244 |
$old_scheds[$name] = array('interval'=>$interval, 'display'=>$display);
|
245 |
update_option('crontrol_schedules', $old_scheds);
|
246 |
}
|
247 |
+
|
248 |
/**
|
249 |
* Deletes a custom cron schedule.
|
250 |
*
|
255 |
unset($scheds[$name]);
|
256 |
update_option('crontrol_schedules', $scheds);
|
257 |
}
|
258 |
+
|
259 |
/**
|
260 |
* Sets up the plugin environment upon first activation.
|
261 |
+
*
|
262 |
* Run using the 'activate_' action.
|
263 |
*/
|
264 |
+
function action_activate() {
|
265 |
$extra_scheds = array('twicedaily'=>array('interval'=>43200, 'display'=>__('Twice Daily', 'crontrol')));
|
266 |
add_option('crontrol_schedules', $extra_scheds);
|
267 |
|
270 |
_set_cron_array(array());
|
271 |
}
|
272 |
}
|
273 |
+
|
274 |
/**
|
275 |
* Adds options & management pages to the admin menu.
|
276 |
*
|
277 |
* Run using the 'admin_menu' action.
|
278 |
*/
|
279 |
+
function action_admin_menu() {
|
280 |
$page = add_options_page('Crontrol', 'Crontrol', 'manage_options', 'crontrol_admin_options_page', array(&$this, 'admin_options_page') );
|
281 |
$page = add_management_page('Crontrol', "Crontrol", 'manage_options', 'crontrol_admin_manage_page', array(&$this, 'admin_manage_page') );
|
282 |
}
|
283 |
+
|
284 |
/**
|
285 |
* Gives WordPress the plugin's set of cron schedules.
|
286 |
*
|
289 |
* @param array $scheds The current cron schedules. Usually an empty array.
|
290 |
* @return array The existing cron schedules along with the plugin's schedules.
|
291 |
*/
|
292 |
+
function filter_cron_schedules($scheds) {
|
293 |
$new_scheds = get_option('crontrol_schedules',array());
|
294 |
return array_merge($new_scheds, $scheds);
|
295 |
}
|
296 |
+
|
297 |
/**
|
298 |
* Displays the options page for the plugin.
|
299 |
*/
|
301 |
$schedules = $this->get_schedules();
|
302 |
$custom_schedules = get_option('crontrol_schedules',array());
|
303 |
$custom_keys = array_keys($custom_schedules);
|
304 |
+
|
305 |
if( isset($_GET['crontrol_message']) ) {
|
306 |
$messages = array( '2' => __("Successfully deleted the cron schedule <b>%s</b>", 'crontrol'),
|
307 |
'3' => __("Successfully added the cron schedule <b>%s</b>", 'crontrol'),
|
311 |
|
312 |
echo "<div id=\"message\" class=\"updated fade\"><p>$msg</p></div>";
|
313 |
}
|
314 |
+
|
315 |
?>
|
316 |
<div class="wrap">
|
317 |
<?php screen_icon(); ?>
|
318 |
<h2><?php _e("Cron Schedules", "crontrol"); ?></h2>
|
319 |
+
<p><?php _e('Cron schedules are the time intervals that are available to WordPress and plugin developers to schedule events. You can only delete cron schedules that you have created with WP Crontrol.', 'crontrol'); ?></p>
|
320 |
<div id="ajax-response"></div>
|
321 |
<table class="widefat">
|
322 |
<thead>
|
324 |
<th><?php _e('Name', 'crontrol'); ?></th>
|
325 |
<th><?php _e('Interval', 'crontrol'); ?></th>
|
326 |
<th><?php _e('Display Name', 'crontrol'); ?></th>
|
327 |
+
<th> </th>
|
328 |
</tr>
|
329 |
</thead>
|
330 |
<tbody>
|
348 |
echo "</tr>";
|
349 |
$class = empty($class)?"alternate":"";
|
350 |
}
|
351 |
+
}
|
352 |
?>
|
353 |
</tbody>
|
354 |
</table>
|
410 |
* Gets the status of WP-Cron functionality on the site by performing a test spawn. Cached for one hour when all is well.
|
411 |
*
|
412 |
*/
|
413 |
+
function test_cron_spawn( $cache = true ) {
|
414 |
|
415 |
if ( defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON )
|
416 |
return true;
|
417 |
|
418 |
$cached_status = get_transient( 'wp-cron-test-ok' );
|
419 |
|
420 |
+
if ( $cache and $cached_status )
|
421 |
return true;
|
422 |
|
423 |
$doing_wp_cron = sprintf( '%.22F', microtime( true ) );
|
525 |
<?php
|
526 |
}
|
527 |
|
528 |
+
function get_cron_events() {
|
529 |
+
|
530 |
+
$crons = _get_cron_array();
|
531 |
+
$events = array();
|
532 |
+
|
533 |
+
if ( empty( $crons ) ) {
|
534 |
+
return new WP_Error(
|
535 |
+
'no_events',
|
536 |
+
__( 'You currently have no cron entries.', 'crontrol' )
|
537 |
+
);
|
538 |
+
}
|
539 |
+
|
540 |
+
foreach( $crons as $time=>$cron ) {
|
541 |
+
foreach( $cron as $hook=>$dings) {
|
542 |
+
foreach( $dings as $sig=>$data ) {
|
543 |
+
|
544 |
+
# This is a prime candidate for a Crontrol_Event class but I'm not bothering currently.
|
545 |
+
$events["$hook-$sig"] = (object) array(
|
546 |
+
'hook' => $hook,
|
547 |
+
'time' => $time,
|
548 |
+
'sig' => $sig,
|
549 |
+
'args' => $data['args'],
|
550 |
+
'schedule' => $data['schedule'],
|
551 |
+
'interval' => $data['interval'],
|
552 |
+
);
|
553 |
+
|
554 |
+
}
|
555 |
+
}
|
556 |
+
}
|
557 |
+
|
558 |
+
return $events;
|
559 |
+
|
560 |
+
}
|
561 |
+
|
562 |
/**
|
563 |
* Displays the manage page for the plugin.
|
564 |
*/
|
575 |
|
576 |
echo "<div id=\"message\" class=\"updated fade\"><p>$msg</p></div>";
|
577 |
}
|
578 |
+
$events = $this->get_cron_events();
|
|
|
579 |
$doing_edit = (isset( $_GET['action']) && $_GET['action']=='edit-cron') ? $_GET['id'] : false ;
|
580 |
$this->show_cron_status();
|
581 |
?>
|
590 |
<th><?php _e('Arguments', 'crontrol'); ?></th>
|
591 |
<th><?php _e('Next Run', 'crontrol'); ?></th>
|
592 |
<th><?php _e('Recurrence', 'crontrol'); ?></th>
|
593 |
+
<th colspan="3"> </th>
|
594 |
</tr>
|
595 |
</thead>
|
596 |
<tbody>
|
597 |
<?php
|
598 |
+
if( is_wp_error($events) ) {
|
599 |
?>
|
600 |
+
<tr><td colspan="7"><?php echo $events->get_error_message(); ?></td></tr>
|
601 |
<?php
|
602 |
} else {
|
603 |
$class = "";
|
604 |
+
foreach( $events as $id=>$event ) {
|
605 |
+
|
606 |
+
if ( $doing_edit && $doing_edit == $event->hook && $event->time == $_GET['next_run'] && $event->sig == $_GET['sig'] ) {
|
607 |
+
$doing_edit = array(
|
608 |
+
'hookname' => $event->hook,
|
609 |
+
'next_run' => $event->time,
|
610 |
+
'schedule' => ( $event->schedule ? $event->schedule : '_oneoff' ),
|
611 |
+
'sig' => $event->sig,
|
612 |
+
'args' => $event->args
|
613 |
+
);
|
614 |
+
}
|
615 |
+
|
616 |
+
echo "<tr id=\"cron-{$id}\" class=\"{$class}\">";
|
617 |
+
echo "<td>".($event->hook==CRONTROL_CRON_JOB ? __('<i>PHP Cron</i>', 'crontrol') : $event->hook)."</td>";
|
618 |
+
echo "<td>".($event->hook==CRONTROL_CRON_JOB ? __('<i>PHP Code</i>', 'crontrol') : $this->json->encode($event->args))."</td>";
|
619 |
+
echo "<td>".strftime("%Y/%m/%d %H:%M:%S", $event->time)." (".$this->time_since(time(), $event->time).")</td>";
|
620 |
+
echo "<td>".($event->schedule ? $event->interval.' ('.$this->interval($event->interval).')' : __('Non-repeating', 'crontrol'))."</td>";
|
621 |
+
echo "<td><a class='view' href='tools.php?page=crontrol_admin_manage_page&action=edit-cron&id={$event->hook}&sig={$event->sig}&next_run={$event->time}#crontrol_form'>Edit</a></td>";
|
622 |
+
echo "<td><a class='view' href='".wp_nonce_url("tools.php?page=crontrol_admin_manage_page&action=run-cron&id={$event->hook}&sig={$event->sig}", "run-cron_{$event->hook}_{$event->sig}")."'>Run Now</a></td>";
|
623 |
+
echo "<td><a class='delete' href='".wp_nonce_url("tools.php?page=crontrol_admin_manage_page&action=delete-cron&id={$event->hook}&sig={$event->sig}&next_run={$event->time}", "delete-cron_{$event->hook}_{$event->sig}_{$event->time}")."'>Delete</a></td>";
|
624 |
+
echo "</tr>";
|
625 |
+
$class = empty($class)?"alternate":"";
|
626 |
+
|
627 |
}
|
628 |
}
|
629 |
?>
|
637 |
$this->show_cron_form((isset($_GET['action']) and $_GET['action']=='new-php-cron'), false);
|
638 |
}
|
639 |
}
|
640 |
+
|
641 |
/**
|
642 |
* Pretty-prints the difference in two times.
|
643 |
*
|
646 |
* @return string The pretty time_since value
|
647 |
* @link http://binarybonsai.com/code/timesince.txt
|
648 |
*/
|
649 |
+
function time_since($older_date, $newer_date) {
|
650 |
return $this->interval( $newer_date - $older_date );
|
651 |
}
|
652 |
+
|
653 |
function interval( $since ) {
|
654 |
// array of time period chunks
|
655 |
$chunks = array(
|
662 |
array( 1 , _n_noop('%s second', '%s seconds', 'crontrol')),
|
663 |
);
|
664 |
|
665 |
+
|
666 |
if( $since <= 0 ) {
|
667 |
return __('now', 'crontrol');
|
668 |
}
|
703 |
|
704 |
return $output;
|
705 |
}
|
706 |
+
|
707 |
+
public function init() {
|
708 |
+
|
709 |
+
static $instance = null;
|
710 |
+
|
711 |
+
if ( !$instance )
|
712 |
+
$instance = new Crontrol;
|
713 |
+
|
714 |
+
return $instance;
|
715 |
+
|
716 |
+
}
|
717 |
+
|
718 |
}
|
719 |
|
720 |
+
class Crontrol_JSON {
|
721 |
+
function encode($in) {
|
722 |
+
return json_encode($in);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
723 |
}
|
724 |
+
function decode($in) {
|
725 |
+
return json_decode($in, true);
|
|
|
|
|
|
|
|
|
|
|
|
|
726 |
}
|
727 |
}
|
728 |
|
729 |
+
if ( defined( 'WP_CLI' ) and WP_CLI and is_readable( $wp_cli = dirname( __FILE__ ) . '/class-wp-cli.php' ) )
|
730 |
+
include_once $wp_cli;
|
731 |
+
|
732 |
// Get this show on the road
|
733 |
+
Crontrol::init();
|
|