root/trunk/trax/vendor/trax/php_shell/shell.php

Revision 313, 42.7 KB (checked in by john, 3 years ago)

updates

Line 
1<?php
2/**
3 *  (PHP 5)
4 *
5 *  @package PHPonTrax
6 *  @version $Id:$
7 *  @copyright (c) 2005 John Peterson
8 *
9 *  Permission is hereby granted, free of charge, to any person obtaining
10 *  a copy of this software and associated documentation files (the
11 *  "Software"), to deal in the Software without restriction, including
12 *  without limitation the rights to use, copy, modify, merge, publish,
13 *  distribute, sublicense, and/or sell copies of the Software, and to
14 *  permit persons to whom the Software is furnished to do so, subject to
15 *  the following conditions:
16 *
17 *  The above copyright notice and this permission notice shall be
18 *  included in all copies or substantial portions of the Software.
19 *
20 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 */
28 
29/*
30vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
31(c) 2006 Jan Kneschke <jan@kneschke.de>
32
33Permission is hereby granted, free of charge, to any person obtaining a copy of
34this software and associated documentation files (the "Software"), to deal in
35the Software without restriction, including without limitation the rights to
36use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
37of the Software, and to permit persons to whom the Software is furnished to do
38so, subject to the following conditions:
39
40The above copyright notice and this permission notice shall be included in all
41copies or substantial portions of the Software.
42
43THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49SOFTWARE.
50*/
51
52/**
53* A interactive PHP Shell
54*
55* The more I work with other languages like python and ruby I like their way how they
56* work on problems. While PHP is very forgiving on errors, it is weak on the debugging
57* side. It was missing a simple to use interactive shell for years. Python and Ruby have
58* their ipython and iruby shell which give you a direct way to interact with the objects.
59* No need to write a script and execute it afterwards.
60*
61* ChangeLog
62*
63*
64* Starting the Shell:
65*
66* If you have a php-cli at hand you can open the shell by defining 'SHELL'
67* and opening the PHP_Shell class file.
68*
69* <code>
70* $ php -v
71* PHP 5.1.4 (cli) (built: May  7 2006 20:52:45)
72* Copyright (c) 1997-2006 The PHP Group
73* Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies
74*
75* $ php -r "define('SHELL', 1); require 'PHP/Shell.php';"
76* </code>
77* If you only have php-cgi write a php-script:
78*
79* <code>
80*     error_reporting(E_ALL);
81*
82*     define("SHELL", 1);
83*     ## in case your terminal support colours:
84*     define("SHELL_HAS_COLOUR", 1);
85*
86*     require "PHP/Shell.php";
87* </code>
88*
89* and execute it with:
90*
91* <pre>
92* $ php -q php-shell.php
93* </pre>
94*
95* Inline Help
96*
97* <pre>
98* PHP-Shell - Version 0.2.0, with readline() support
99* (c) 2006, Jan Kneschke <jan@kneschke.de>
100* released under the terms of the PHP License 2.0
101*
102* >> use '?' to open the inline help
103*
104* >> ?
105* "inline help for the PHP-shell
106*
107*   >> ?
108*     print this help
109*   >> ? <topic>
110*     get the doccomment for a class, method, property or function
111*   >> p <var>
112*     execute a verbose print (if implemented)
113*   >> quit
114*     leave shell
115* "
116* >> ? PHP_Shell
117* </pre>
118* Alternatives
119*
120* - http://david.acz.org/phpa/
121* - http://www.hping.org/phpinteractive/
122* - the embedded interactive php-shell: $ php -a
123*
124* @package PHP
125*/
126
127/**
128* PHP_Shell
129*
130* a interactive PHP Shell with tab-completion and history
131* it can catch FATAL errors before executing the code
132*
133* to customize the operation of the shell you can either
134* extend the PHP_Shell class or declare a external autoload
135* or error-handler. If you want to use your own print-out
136* functions declare __shell_print_vars().
137*
138* - __shell_error_handler()
139* - __autoload()
140* - __shell_print_vars()
141*
142* To keep the namespace clashing between shell and your program
143* as small as possible all public variables and functions from
144* the shell are prefixed with __shell:
145*
146* - $__shell is the object of the shell
147*   can be read, this is the shell object itself, don't touch it
148* - $__shell_retval is the return value of the eval() before
149*   it is printed
150*   can't be read, but overwrites existing vars with this name
151* - $__shell_exception is the catched Exception on Warnings, Notices, ..
152*   can't be read, but overwrites existing vars with this name
153*
154* @package PHP
155*/
156
157require_once("php_shell/shell_prototypes.php");
158
159class PHP_Shell {
160    /**
161    * current code-buffer
162    * @var string
163    */
164    protected $code; 
165
166    /**
167    * set if 'p ...' is executed
168    * @var bool
169    */
170    protected $verbose; 
171
172    /**
173    * set if readline support is enabled
174    * @var bool
175    */
176    protected $have_readline; 
177
178    /**
179    * current version of the class
180    * @var string
181    */
182    protected $version = '0.2.7';
183
184    /**
185    * registered commands
186    *
187    * @var array
188    * @see registerCommand
189    */
190    protected $commands;
191
192    /**
193    * does the use want to use the internal autoload ?
194    *
195    * @var bool
196    */
197    protected $autoload = false;
198   
199
200    /**
201    * shell colours
202    *
203    * @var array
204    * @see setColourScheme
205    */
206    protected $colours;
207
208    /**
209    * shell colour schemes
210    *
211    * @var array
212    * @see registerColourScheme
213    */
214    protected $colour_scheme;     
215   
216    protected $virtual_methods = array('find_by', 'find_all_by', 'count_all', 'sum_all', 'avg_all');
217
218    # shell colours
219    const C_RESET = "\033[0m";
220
221    const C_BLACK = "\033[0;30m";
222    const C_RED = "\033[0;31m";
223    const C_GREEN = "\033[0;32m";
224    const C_BROWN = "\033[0;33m";
225    const C_BLUE = "\033[0;34m";
226    const C_PURPLE = "\033[0;35m";
227    const C_CYAN = "\033[0;36m";
228    const C_LIGHT_GRAY = "\033[0;37m";
229
230    const C_GRAY = "\033[1;30m";
231    const C_LIGHT_RED = "\033[1;31m";
232    const C_LIGHT_GREEN = "\033[1;32m";
233    const C_YELLOW = "\033[1;33m";
234    const C_LIGHT_BLUE = "\033[1;34m";
235    const C_LIGHT_PURPLE = "\033[1;35m";
236    const C_LIGHT_CYAN = "\033[1;36m";
237    const C_WHITE = "\033[1;37m";
238
239    /**
240    * init the shell and change if readline support is available
241    */ 
242    public function __construct() {
243        $this->code = '';
244        $this->vars = array();
245
246        $this->stdin = null;
247
248        $this->have_readline = function_exists('readline');
249
250        if ($this->have_readline) {
251            readline_completion_function('__shell_readline_complete');
252        }
253
254        $this->use_readline = true;
255
256        $this->commands = array();
257
258        $this->registerCommand('#^quit$#', 'cmdQuit', 'quit', 'leaves the shell');
259        $this->registerCommand('#^\?$#', 'cmdHelp', '?', 'show this help');
260        $this->registerCommand('#^\? #', 'cmdHelp', '? <var>', 'show the DocComment a Class, Method or Function'.PHP_EOL.'    e.g.: ? fopen(), ? PHP_Shell, ? $__shell');
261        $this->registerCommand('#^p #', 'cmdPrint', 'p <var>', 'print the variable verbosly');
262        $this->registerCommand('#^:set #', 'cmdSet', ':set <var>', 'set a shell variable');
263
264        $this->registerColourScheme(
265            "plain", array( 
266                "default"   => "", "value"     => "",
267                "exception" => "", "reset"     => ""));
268
269        $this->registerColourScheme(
270            "dark", array( 
271                "default"   => PHP_SHELL::C_YELLOW, 
272                "value"     => PHP_SHELL::C_WHITE,
273                "exception" => PHP_SHELL::C_PURPLE));
274
275        $this->registerColourScheme(
276            "light", array( 
277                "default"   => PHP_SHELL::C_BLACK, 
278                "value"     => PHP_SHELL::C_BLUE,
279                "exception" => PHP_SHELL::C_RED));
280
281    }
282
283    /**
284    * register your own command for the shell
285    *
286    * @param string $regex a regex to match against the input line
287    * @param string $callback a method in this class to call of the regex matches
288    * @param string $cmd the command string for the help
289    * @param string $help the full help description for this command
290    */
291    public function registerCommand($regex, $callback, $cmd, $help) {
292        $this->commands[] = array(
293            'regex' => $regex,
294            'method' => $callback,
295            'command' => $cmd,
296            'description' => $help
297        );
298    } 
299   
300    public function classMethodExists($object, $method) {
301        if(preg_grep("/^(find|find_all)_by.*$/", $this->virtual_methods) || method_exists($object, $method)) {
302            return true;
303        }               
304        return false;
305    }
306   
307    /**
308    * parse the PHP code
309    *
310    * we parse before we eval() the code to
311    * - fetch fatal errors before they come up
312    * - know about where we have to wait for closing braces
313    *
314    * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise
315    */
316    public function parse() {
317        ## remove empty lines
318        $this->code = trim($this->code);
319        if ($this->code == '') return 1;
320
321        $t = token_get_all('<?php '.$this->code.' ?>');
322 
323        $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */
324        $need_return = 1;    /* can we prepend a return to the eval-string ? */
325        $eval = '';          /* code to be eval()'ed later */
326        $braces = array();   /* to track if we need more closing braces */
327
328        $methods = array();  /* to track duplicate methods in a class declaration */
329        $ts = array();       /* tokens without whitespaces */
330       
331        foreach ($t as $ndx => $token) {
332            if (is_array($token)) {
333                $ignore = 0;
334     
335                switch($token[0]) {
336                case T_WHITESPACE:
337                case T_OPEN_TAG:
338                case T_CLOSE_TAG:
339                    $ignore = 1;
340                    break;
341                case T_FOREACH:
342                case T_DO:
343                case T_WHILE:
344                case T_FOR:
345
346                case T_IF:
347                case T_RETURN:
348                   
349                case T_CLASS:
350                case T_FUNCTION:
351                case T_INTERFACE:
352
353                case T_PRINT:
354                case T_ECHO:
355
356                case T_COMMENT:
357                case T_UNSET:
358
359                case T_INCLUDE:
360                case T_REQUIRE:
361                case T_INCLUDE_ONCE:
362                case T_REQUIRE_ONCE:
363                case T_TRY:
364                    $need_return = 0;
365                    break;
366                case T_VARIABLE:
367                case T_STRING:
368                case T_NEW:
369                case T_EXTENDS:
370                case T_IMPLEMENTS:
371                case T_OBJECT_OPERATOR:
372                case T_DOUBLE_COLON:
373                case T_INSTANCEOF:
374
375                case T_CATCH:
376
377                case T_ELSE:
378                case T_AS:
379                case T_LNUMBER:
380                case T_DNUMBER:
381                case T_CONSTANT_ENCAPSED_STRING:
382                case T_ENCAPSED_AND_WHITESPACE:
383                case T_CHARACTER:
384                case T_ARRAY:
385                case T_DOUBLE_ARROW:
386
387                case T_CONST:
388                case T_PUBLIC:
389                case T_PROTECTED:
390                case T_PRIVATE:
391                case T_ABSTRACT:
392                case T_STATIC:
393                case T_VAR:
394
395                case T_INC:
396                case T_DEC:
397                case T_SL:
398                case T_SL_EQUAL:
399                case T_SR:
400                case T_SR_EQUAL:
401
402                case T_IS_EQUAL:
403                case T_IS_IDENTICAL:
404                case T_IS_GREATER_OR_EQUAL:
405                case T_IS_SMALLER_OR_EQUAL:
406                   
407                case T_BOOLEAN_OR:
408                case T_LOGICAL_OR:
409                case T_BOOLEAN_AND:
410                case T_LOGICAL_AND:
411                case T_LOGICAL_XOR:
412                case T_MINUS_EQUAL:
413                case T_PLUS_EQUAL:
414                case T_MUL_EQUAL:
415                case T_DIV_EQUAL:
416                case T_MOD_EQUAL:
417                case T_XOR_EQUAL:
418                case T_AND_EQUAL:
419                case T_OR_EQUAL:
420
421                case T_FUNC_C:
422                case T_CLASS_C:
423                case T_LINE:
424                case T_FILE:
425
426                    /* just go on */
427                    break;
428                default:
429                    /* debug unknown tags*/
430                    error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1]));
431                   
432                    break;
433                }
434                if (!$ignore) {
435                    $eval .= $token[1]." ";
436                    $ts[] = array("token" => $token[0], "value" => $token[1]);
437                }
438            } else {
439                $ts[] = array("token" => $token, "value" => '');
440
441                $last = count($ts) - 1;
442
443                switch ($token) {
444                case '(':
445                    /* walk backwards through the tokens */
446
447                    if ($last >= 3 &&
448                        $ts[$last - 1]['token'] == T_STRING &&
449                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
450                        $ts[$last - 3]['token'] == T_VARIABLE ) {
451
452                        /* $object->method( */
453
454                        /* $object has to exist and has to be a object */
455                        $objname = $ts[$last - 3]['value'];
456                       
457                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
458                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
459                        }
460                        $object = $GLOBALS[ltrim($objname, '$')];
461
462                        if (!is_object($object)) {
463                            throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
464                        }
465                       
466                        $method = $ts[$last - 1]['value'];
467
468                        /* obj */
469                       
470                        if (!$this->classMethodExists($object, $method)) {
471                            throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", 
472                                $objname, get_class($object), $method));
473                        }
474                    } else if ($last >= 3 &&
475                        $ts[$last - 1]['token'] == T_VARIABLE &&
476                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
477                        $ts[$last - 3]['token'] == T_VARIABLE ) {
478
479                        /* $object->$method( */
480
481                        /* $object has to exist and has to be a object */
482                        $objname = $ts[$last - 3]['value'];
483                       
484                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
485                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
486                        }
487                        $object = $GLOBALS[ltrim($objname, '$')];
488
489                        if (!is_object($object)) {
490                            throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
491                        }
492                       
493                        $methodname = $ts[$last - 1]['value'];
494
495                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
496                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
497                        }
498                        $method = $GLOBALS[ltrim($methodname, '$')];
499
500                        /* obj */
501                       
502                        if (!in_array($method, $this->virtual_methods) && !method_exists($object, $method)) {
503                            throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", 
504                                $objname, get_class($object), $method));
505                        }
506
507                    } else if ($last >= 6 &&
508                        $ts[$last - 1]['token'] == T_STRING &&
509                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
510                        $ts[$last - 3]['token'] == ']' &&
511                            /* might be anything as index */
512                        $ts[$last - 5]['token'] == '[' &&
513                        $ts[$last - 6]['token'] == T_VARIABLE ) {
514
515                        /* $object[...]->method( */
516
517                        /* $object has to exist and has to be a object */
518                        $objname = $ts[$last - 6]['value'];
519                       
520                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
521                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
522                        }
523                        $array = $GLOBALS[ltrim($objname, '$')];
524
525                        if (!is_array($array)) {
526                            throw new Exception(sprintf('Variable \'%s\' is not a array', $objname));
527                        }
528
529                        $andx = $ts[$last - 4]['value'];
530
531                        if (!isset($array[$andx])) {
532                            throw new Exception(sprintf('%s[\'%s\'] is not set', $objname, $andx));
533                        }
534
535                        $object = $array[$andx];
536
537                        if (!is_object($object)) {
538                            throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
539                        }
540                       
541                        $method = $ts[$last - 1]['value'];
542
543                        /* obj */
544                       
545                        if (!in_array($method, $this->virtual_methods) && !method_exists($object, $method)) {
546                            throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'", 
547                                $objname, get_class($object), $method));
548                        }
549
550                    } else if ($last >= 3 &&
551                        $ts[$last - 1]['token'] == T_STRING &&
552                        $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
553                        $ts[$last - 3]['token'] == T_STRING ) {
554
555                        /* Class::method() */
556
557                        /* $object has to exist and has to be a object */
558                        $classname = $ts[$last - 3]['value'];
559                       
560                        if (!class_exists($classname)) {
561                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
562                        }
563                       
564                        $method = $ts[$last - 1]['value'];
565
566                        if (!in_array($method, $this->virtual_methods) && !in_array($method, get_class_methods($classname))) {
567                            throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'", 
568                                $classname, $method));
569                        }
570                    } else if ($last >= 3 &&
571                        $ts[$last - 1]['token'] == T_VARIABLE &&
572                        $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
573                        $ts[$last - 3]['token'] == T_STRING ) {
574
575                        /* Class::method() */
576
577                        /* $object has to exist and has to be a object */
578                        $classname = $ts[$last - 3]['value'];
579                       
580                        if (!class_exists($classname)) {
581                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
582                        }
583                       
584                        $methodname = $ts[$last - 1]['value'];
585
586                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
587                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
588                        }
589                        $method = $GLOBALS[ltrim($methodname, '$')];
590
591                        if (!in_array($method, $this->virtual_methods) && !in_array($method, get_class_methods($classname))) {
592                            throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'", 
593                                $classname, $method));
594                        }
595
596                    } else if ($last >= 2 &&
597                        $ts[$last - 1]['token'] == T_STRING &&
598                        $ts[$last - 2]['token'] == T_NEW ) {
599
600                        /* new Class() */
601
602                        $classname = $ts[$last - 1]['value'];
603
604                        if (!class_exists($classname)) {
605                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
606                        }
607
608                        $r = new ReflectionClass($classname);
609
610                        if ($r->isAbstract()) {
611                            throw new Exception(sprintf("Can't instantiate abstract Class '%s'", $classname));
612                        }
613
614                        if (!$r->isInstantiable()) {
615                            throw new Exception(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname));
616                        }
617
618                    } else if ($last >= 2 &&
619                        $ts[0]['token'] != T_CLASS &&
620                        $ts[$last - 1]['token'] == T_STRING &&
621                        $ts[$last - 2]['token'] == T_FUNCTION ) {
622
623                        /* make sure we are not a in class definition */
624
625                        /* function a() */
626
627                        $func = $ts[$last - 1]['value'];
628
629                        if (function_exists($func)) {
630                            throw new Exception(sprintf('Function \'%s\' is already defined', $func));
631                        }
632                    } else if ($last >= 4 &&
633                        $ts[0]['token'] == T_CLASS &&
634                        $ts[1]['token'] == T_STRING &&
635                        $ts[$last - 1]['token'] == T_STRING &&
636                        $ts[$last - 2]['token'] == T_FUNCTION ) {
637
638                        /* make sure we are not a in class definition */
639
640                        /* class a { .. function a() ... } */
641
642                        $func = $ts[$last - 1]['value'];
643                        $classname = $ts[1]['value'];
644
645                        if (isset($methods[$func])) {
646                            throw new Exception(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname));
647                        }
648
649                        $methods[$func] = 1;
650
651                    } else if ($last >= 1 &&
652                        $ts[$last - 1]['token'] == T_STRING ) {
653                        /* func() */
654                        $funcname = $ts[$last - 1]['value'];
655                       
656                        if (!function_exists($funcname)) {
657                            throw new Exception(sprintf("Function %s() doesn't exist", $funcname));
658                        }
659                    } else if ($last >= 1 &&
660                        $ts[$last - 1]['token'] == T_VARIABLE ) {
661   
662                        /* $object has to exist and has to be a object */
663                        $funcname = $ts[$last - 1]['value'];
664                       
665                        if (!isset($GLOBALS[ltrim($funcname, '$')])) {
666                            throw new Exception(sprintf('Variable \'%s\' is not set', $funcname));
667                        }
668                        $func = $GLOBALS[ltrim($funcname, '$')];
669
670                        if (!function_exists($func)) {
671                            throw new Exception(sprintf("Function %s() doesn't exist", $func));
672                        }
673
674                    }
675                   
676                    array_push($braces, $token);
677                    break;
678                case '{':
679                    $need_return = 0;
680
681                    if ($last >= 2 &&
682                        $ts[$last - 1]['token'] == T_STRING &&
683                        $ts[$last - 2]['token'] == T_CLASS ) {
684
685                        /* class name { */
686
687                        $classname = $ts[$last - 1]['value'];
688
689                        if (class_exists($classname, false)) {
690                            throw new Exception(sprintf("Class '%s' can't be redeclared", $classname));
691                        }
692                    } else if ($last >= 4 &&
693                        $ts[$last - 1]['token'] == T_STRING &&
694                        $ts[$last - 2]['token'] == T_EXTENDS &&
695                        $ts[$last - 3]['token'] == T_STRING &&
696                        $ts[$last - 4]['token'] == T_CLASS ) {
697
698                        /* class classname extends classname { */
699
700                        $classname = $ts[$last - 3]['value'];
701                        $extendsname = $ts[$last - 1]['value'];
702
703                        if (class_exists($classname, false)) {
704                            throw new Exception(sprintf("Class '%s' can't be redeclared", 
705                                $classname));
706                        }
707                        if (!class_exists($extendsname, false)) {
708                            throw new Exception(sprintf("Can't extend '%s' from not existing Class '%s'", 
709                                $classname, $extendsname));
710                        }
711                    } else if ($last >= 4 &&
712                        $ts[$last - 1]['token'] == T_STRING &&
713                        $ts[$last - 2]['token'] == T_IMPLEMENTS &&
714                        $ts[$last - 3]['token'] == T_STRING &&
715                        $ts[$last - 4]['token'] == T_CLASS ) {
716
717                        /* class name implements interface { */
718
719                        $classname = $ts[$last - 3]['value'];
720                        $implements = $ts[$last - 1]['value'];
721
722                        if (class_exists($classname, false)) {
723                            throw new Exception(sprintf("Class '%s' can't be redeclared", 
724                                $classname));
725                        }
726                        if (!interface_exists($implements, false)) {
727                            throw new Exception(sprintf("Can't implement not existing Interface '%s' for Class '%s'", 
728                                $implements, $classname));
729                        }
730                    }
731
732                    array_push($braces, $token);
733                    break;
734                case '}':
735                    $need_return = 0;
736                case ')':
737                    array_pop($braces);
738                    break;
739                }
740                 
741                $eval .= $token;
742            }   
743        }
744
745        $last = count($ts) - 1;
746        if ($last >= 2 &&
747            $ts[$last - 0]['token'] == T_STRING &&
748            $ts[$last - 1]['token'] == T_DOUBLE_COLON &&
749            $ts[$last - 2]['token'] == T_STRING ) {
750
751            /* Class::constant */
752
753            /* $object has to exist and has to be a object */
754            $classname = $ts[$last - 2]['value'];
755           
756            if (!class_exists($classname)) {
757                throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
758            }
759           
760            $constname = $ts[$last - 0]['value'];
761
762            $c = new ReflectionClass($classname);
763            if (!$c->hasConstant($constname)) {
764                throw new Exception(sprintf("Class '%s' doesn't have a constant named '%s'", 
765                    $classname, $constname));
766            }
767        } else if ($last == 0 &&
768            $ts[$last - 0]['token'] == T_VARIABLE ) {
769
770            /* $var */
771
772            $varname = $ts[$last - 0]['value'];
773           
774            if (!isset($GLOBALS[ltrim($varname, '$')])) {
775                throw new Exception(sprintf('Variable \'%s\' is not set', $varname));
776            }
777        }
778
779
780        $need_more = count($braces);
781       
782        if ($need_more || ';' === $token) {
783            $need_semicolon = 0;
784        } 
785 
786        if ($need_return) {
787            $eval = "return ".$eval;
788        }
789 
790        /* add a traling ; if necessary */ 
791        if ($need_semicolon) $eval .= ';';
792       
793        if (!$need_more) {
794            $this->code = $eval;
795        }
796               
797        return $need_more;
798    }
799   
800    /**
801    * show the prompt and fetch a single line
802    *
803    * uses readline() if avaialbe
804    *
805    * @return string a input-line
806    */
807    public function readline() {
808        if (empty($this->code)) print PHP_EOL;
809
810        $prompt = (empty($this->code)) ? '>> ' : '.. ';
811
812        if ($this->have_readline) {
813            $l = readline($prompt);
814
815            readline_add_history($l);
816        } else {
817            print $prompt;
818
819            if (is_null($this->stdin)) {
820                if (false === ($this->stdin = fopen("php://stdin", "r"))) {
821                    return false;
822                }
823            } 
824                                 
825            $l = fgets($this->stdin); 
826        }
827        return $l;
828    }
829
830    /**
831    * get the inline help
832    *
833    * @return string the inline help as string
834    */
835    public function getHelp() {
836        $o = 'Inline Help:'.PHP_EOL;
837
838        foreach ($this->commands as $cmd) {
839            $o .= sprintf('  >> %s'.PHP_EOL.'    %s'.PHP_EOL,
840                $cmd['command'],
841                $cmd['description']
842            );
843        }
844
845        return $o;
846    }
847
848    /**
849    * handle the 'quit' command
850    *
851    * @return bool false to leave the input() call
852    * @see input
853    */
854    protected function cmdQuit($l) {
855        return false;
856    }
857
858    /**
859    * handle the 'p ' command
860    *
861    * set the verbose flag
862    *
863    * @return string the pure command-string without the 'p ' command
864    */
865    protected function cmdPrint($l) {
866        $this->verbose = 1;
867        $cmd = substr($l, 2);
868
869        return $cmd;
870    }
871
872    /**
873    * handle the '?' commands
874    *
875    * With the help of the Reflection Class we extract the DocComments and display them
876    * For internal Functions we extract the prototype from the php source.
877    *
878    * ? Class::method()
879    * ? $obj->method()
880    * ? Class::property
881    * ? $obj::property
882    * ? Class
883    * ? $obj
884    * ? function()
885    *
886    * The license of the PHP_Shell class
887    * ? license
888    *
889    * @return string the help text
890    */
891    protected function cmdHelp($l) {
892        if ("? " == substr($l, 0, strlen("? "))) {
893            $str = substr($l, 2);
894
895            $cmd = '';
896           
897            if (preg_match('#^([A-Za-z0-9_]+)::([a-zA-Z0-9_]+)\(\s*\)\s*#', $str, $a)) {
898                /* ? Class::method() */
899
900                $class = $a[1];
901                $method = $a[2];
902
903                if (false !== ($proto = PHP_ShellPrototypes::getInstance()->get($class.'::'.$method))) {
904
905                    $cmd = sprintf("/**\n* %s\n\n* @params %s\n* @return %s\n*/\n",
906                        $proto['description'],
907                        $proto['params'],
908                        $proto['return']
909                    );
910                } else if (class_exists($class, false)) {
911                    $c = new ReflectionClass($class);
912
913                    if ($c->hasMethod($method)) {
914                        $cmd = $c->getMethod($method)->getDocComment();
915                    }
916                }
917            } else if (preg_match('#^\$([A-Za-z0-9_]+)->([a-zA-Z0-9_]+)\(\s*\)\s*#', $str, $a)) {
918                /* ? $obj->method() */
919                if (isset($GLOBALS[$a[1]]) && is_object($GLOBALS[$a[1]])) {
920                    $class = get_class($GLOBALS[$a[1]]);
921                    $method = $a[2];
922                   
923                    $c = new ReflectionClass($class);
924   
925                    if ($c->hasMethod($method)) {
926                        $cmd = $c->getMethod($method)->getDocComment();
927                    }
928                }
929            } else if (preg_match('#^([A-Za-z0-9_]+)::([a-zA-Z0-9_]+)\s*$#', $str, $a)) { 
930                /* ? Class::property */
931                $class = $a[1];
932                $property = $a[2];
933                if (class_exists($class, false)) {
934                    $c = new ReflectionClass($class);
935
936                    if ($c->hasProperty($property)) {
937                        $cmd = $c->getProperty($property)->getDocComment();
938                    }
939                }
940            } else if (preg_match('#^\$([A-Za-z0-9_]+)->([a-zA-Z0-9_]+)\s*$#', $str, $a)) { 
941                /* ? $obj->property */
942                if (isset($GLOBALS[$a[1]]) && is_object($GLOBALS[$a[1]])) {
943                    $class = get_class($GLOBALS[$a[1]]);
944                    $method = $a[2];
945                   
946                    $c = new ReflectionClass($class);
947
948                    if ($c->hasProperty($property)) {
949                        $cmd = $c->getProperty($property)->getDocComment();
950                    }
951
952                }
953            } else if (preg_match('#^([A-Za-z0-9_]+)$#', $str, $a)) {
954                /* ? Class */
955                if (class_exists($a[1], false)) {
956                    $c = new ReflectionClass($a[1]);
957                    $cmd = $c->getDocComment();
958                } else if ($a[1] == 'license') {
959                    $cmd = <<<EOF
960(c) 2006 Jan Kneschke <jan@kneschke.de>
961
962Permission is hereby granted, free of charge, to any person obtaining a copy of
963this software and associated documentation files (the "Software"), to deal in
964the Software without restriction, including without limitation the rights to
965use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
966of the Software, and to permit persons to whom the Software is furnished to do
967so, subject to the following conditions:
968
969The above copyright notice and this permission notice shall be included in all
970copies or substantial portions of the Software.
971
972THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
973IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
974FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
975AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
976LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
977OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
978SOFTWARE.
979
980EOF;
981                }
982            } else if (preg_match('#^\$([A-Za-z0-9_]+)$#', $str, $a)) {
983                /* ? $object */
984                $obj = $a[1];
985                if (isset($GLOBALS[$obj]) && is_object($GLOBALS[$obj])) {
986                    $class = get_class($GLOBALS[$obj]);
987
988                    $c = new ReflectionClass($class);
989                    $cmd = $c->getDocComment();
990                }
991
992            } else if (preg_match('#^([A-Za-z0-9_]+)\(\s*\)$#', $str, $a)) {
993                /* ? function() */
994                $func = $a[1];
995
996                if (false !== ($proto = PHP_ShellPrototypes::getInstance()->get($func))) {
997                    $cmd = sprintf("/**\n* %s\n*\n* @params %s\n* @return %s\n*/\n",
998                        $proto['description'],
999                        $proto['params'],
1000                        $proto['return']
1001                    );
1002                } else if (function_exists($func)) {
1003                    $c = new ReflectionFunction($func);
1004                    $cmd = $c->getDocComment();
1005                }
1006            }
1007
1008            if ($cmd == '') {
1009                $cmd = var_export(sprintf('no help found for \'%s\'', $str), 1);
1010            } else {
1011                $cmd = var_export($cmd, 1);
1012            }
1013        } else if ("?" == $l) {
1014            $cmd = $this->getHelp();
1015            $cmd = var_export($cmd, 1);
1016        }
1017
1018        return $cmd;
1019    }
1020
1021    /**
1022    * set a shell-var
1023    *
1024    * :set al to enable autoload
1025    * :set bg=dark to enable highlighting with a dark backgroud
1026    */
1027    public function cmdSet($l) {
1028        if (!preg_match('#:set\s+([a-z]+)\s*(?:=\s*([a-z0-9]+)\s*)?$#i', $l, $a)) {
1029            print('unknown :set syntax');
1030            return;
1031        }
1032
1033        $key = $a[1];
1034
1035        switch ($key) {
1036        case 'bg':
1037        case 'background':
1038            if (!isset($a[2])) {
1039                print(':set '.$key.' failed: a value is required, example: :set '.$key.'=dark');
1040                return;
1041            }
1042            if (false == $this->applyColourScheme($a[2])) {
1043                print('setting colourscheme failed: colourscheme '.$a[2].' is unknown');
1044                return;
1045            }
1046            break;
1047        case 'al':
1048        case 'autoload':
1049            if (function_exists('__autoload')) {
1050                print('can\'t enabled autoload as a external __autoload() function is already defined');
1051                return;
1052            }
1053
1054            if ($this->autoload) {
1055                print('autload is already enabled');
1056                return;
1057            }
1058
1059            $this->autoload = true;
1060            break;
1061        default:
1062            print (':set '.$key.' failed: unknown key');
1063            return;
1064        }
1065    }
1066   
1067    /**
1068    * handle the input line
1069    *
1070    * read the input and handle the commands of the shell
1071    *
1072    * @return bool false on 'quit' or EOF, true otherwise
1073    */
1074    public function input() {
1075        $l = $this->readline();
1076
1077        /* got EOF ? */
1078        if (false === $l) return false;
1079
1080        $l = trim($l);
1081       
1082        if (empty($this->code)) {
1083            $this->verbose = 0;
1084
1085            foreach ($this->commands as $cmd) {
1086                if (preg_match($cmd['regex'], $l)) {
1087                    $func = $cmd['method'];
1088
1089                    if (false === ($l = $this->$func($l))) {
1090                        ## quit
1091                        return false;
1092                    }
1093                    break;
1094                }
1095            }
1096        }
1097       
1098        $this->appendCode($l); 
1099
1100        return true;
1101    }
1102
1103    public function isAutoloadEnabled() {
1104        return $this->autoload;
1105    }
1106   
1107    /**
1108    * get the code-buffer
1109    *
1110    * @return string the code-buffer
1111    */
1112    public function getCode() {
1113        return $this->code;
1114    }
1115   
1116    /**
1117    * reset the code-buffer
1118    */
1119    public function resetCode() {
1120        $this->code = '';
1121    }
1122 
1123    /**
1124    * append code to the code-buffer
1125    *
1126    * @param string $code input buffer
1127    */
1128    public function appendCode($code) {
1129        $this->code .= $code;
1130    }
1131   
1132    /**
1133    * check if we have a verbose print-out
1134    *
1135    * @return bool 1 if verbose, 0 otherwise
1136    */
1137    public function getVerbose() {
1138        return $this->verbose;
1139    }
1140
1141    /**
1142    * check if readline support is enabled
1143    *
1144    * @return bool true if enabled, false otherwise
1145    */
1146    public function hasReadline() {
1147        return $this->have_readline;
1148    }
1149
1150    /**
1151    * get version of the class
1152    *
1153    * @return string version-string
1154    */
1155    public function getVersion() {
1156        return $this->version;
1157    }
1158
1159    /**
1160    * get a colour for the shell
1161    *
1162    * @param string $type one of (value|exception|reset|default)
1163    * @return string a colour string or a empty string
1164    */
1165    public function getColour($type) {
1166        return isset($this->colour[$type]) ? $this->colour[$type] : '';
1167    }
1168
1169    /**
1170    * apply a colour scheme to the current shell
1171    *
1172    * @param string $scheme name of the scheme
1173    * @return false if colourscheme is not known, otherwise true
1174    */
1175    public function applyColourScheme($scheme) {
1176        if (!isset($this->colour_scheme[$scheme])) return false;
1177
1178        $this->colour = $this->colour_scheme[$scheme];
1179
1180        return true;
1181    }
1182
1183    /**
1184    * registers a colour scheme
1185    *
1186    * @param string $scheme name of the colour scheme
1187    * @param array a array of colours
1188    */
1189    public function registerColourScheme($scheme, $colours) {
1190        if (!is_array($colours)) return;
1191
1192        /* set a reset colour if it is not supplied from the outside */
1193        if (!isset($colours["reset"])) $colours["reset"] = PHP_SHELL::C_RESET;
1194
1195        $this->colour_scheme[$scheme] = $colours;
1196    }
1197}
1198
1199/**
1200* a readline completion callback
1201*
1202* @param string $str linebuffer
1203* @param integer $pos position in linebuffer
1204* @return array list of possible matches
1205*/
1206function __shell_readline_complete($str, $pos) {
1207    $in = readline_info('line_buffer');
1208
1209    /**
1210    * parse the line-buffer backwards to see if we have a
1211    * - constant
1212    * - function
1213    * - variable
1214    */
1215
1216    $m = array();
1217
1218    if (preg_match('#\$([A-Za-z0-9_]+)->#', $in, $a)) {
1219        /* check for $o->... */
1220        $name = $a[1];
1221
1222        if (isset($GLOBALS[$name]) && is_object($GLOBALS[$name])) {
1223            $c = get_class_methods($GLOBALS[$name]);
1224
1225            foreach ($c as $v) {
1226                $m[] = $v.'(';
1227            }
1228            $c = get_class_vars(get_class($GLOBALS[$name]));
1229
1230            foreach ($c as $k => $v) {
1231                $m[] = $k;
1232            }
1233
1234            return $m;
1235        }
1236    } else if (preg_match('#\$([A-Za-z0-9_]+)\[([^\]]+)\]->#', $in, $a)) {
1237        /* check for $o[...]->... */
1238        $name = $a[1];
1239
1240        if (isset($GLOBALS[$name]) && 
1241            is_array($GLOBALS[$name]) &&
1242            isset($GLOBALS[$name][$a[2]])) {
1243
1244            $c = get_class_methods($GLOBALS[$name][$a[2]]);
1245
1246            foreach ($c as $v) {
1247                $m[] = $v.'(';
1248            }
1249            $c = get_class_vars(get_class($GLOBALS[$name][$a[2]]));
1250
1251            foreach ($c as $k => $v) {
1252                $m[] = $k;
1253            }
1254            return $m;
1255        }
1256
1257    } else if (preg_match('#([A-Za-z0-9_]+)::#', $in, $a)) {
1258        /* check for Class:: */
1259        $name = $a[1];
1260
1261        if (class_exists($name, false)) {
1262            $c = get_class_methods($name);
1263
1264            foreach ($c as $v) {
1265                $m[] = sprintf('%s::%s(', $name, $v);
1266            }
1267
1268            $cl = new ReflectionClass($name);
1269            $c = $cl->getConstants();
1270
1271            foreach ($c as $k => $v) {
1272                $m[] = sprintf('%s::%s', $name, $k);
1273            }
1274
1275            return $m;
1276        }
1277    } else if (preg_match('#\$([a-zA-Z]?[a-zA-Z0-9_]*)$#', $in)) {
1278        $m = array_keys($GLOBALS);
1279
1280        return $m;
1281    } else if (preg_match('#new #', $in)) {
1282        $c = get_declared_classes();
1283   
1284        foreach ($c as $v) {
1285            $m[] = $v.'(';
1286        }
1287
1288        return $m;
1289    } else if (preg_match('#^:set #', $in)) {
1290        $m[] = 'autoload';
1291        $m[] = 'background=';
1292
1293        return $m;
1294    }
1295
1296    $f = get_defined_functions();
1297
1298    foreach ($f['internal'] as $v) {
1299        $m[] = $v.'(';
1300    }
1301
1302    foreach ($f['user'] as $v) {
1303        $m[] = $v.'(';
1304    }
1305   
1306    $c = get_declared_classes();
1307
1308    foreach ($c as $v) {
1309        $m[] = $v.'::';
1310    }
1311
1312    $c = get_defined_constants();
1313
1314    foreach ($c as $k => $v) {
1315        $m[] = $k;
1316    }
1317
1318    $m[] = 'foreach (';
1319    $m[] = 'require';
1320
1321    # printf("%s ... %s\n", $str, $pos);
1322    return $m;
1323}
1324
1325
Note: See TracBrowser for help on using the browser.