Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/*
5
(c) 2006 Jan Kneschke <jan@kneschke.de>
6
 
7
Permission is hereby granted, free of charge, to any person obtaining a copy of
8
this software and associated documentation files (the "Software"), to deal in
9
the Software without restriction, including without limitation the rights to
10
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11
of the Software, and to permit persons to whom the Software is furnished to do
12
so, subject to the following conditions:
13
 
14
The above copyright notice and this permission notice shall be included in all
15
copies or substantial portions of the Software.
16
 
17
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
SOFTWARE.
24
*/
25
 
26
/**
27
* A interactive PHP Shell
28
*
29
* The more I work with other languages like python and ruby I like their way how they
30
* work on problems. While PHP is very forgiving on errors, it is weak on the debugging
31
* side. It was missing a simple to use interactive shell for years. Python and Ruby have
32
* their ipython and iruby shell which give you a direct way to interact with the objects.
33
* No need to write a script and execute it afterwards.
34
*
35
* Starting the Shell:
36
*
37
* The package contains a shell wrapper for windows and unix:
38
* <pre>
39
* sh>  php-shell.sh
40
* win> php-shell
41
* </pre>
42
*
43
* Both are calling the wrapper script <code>php -q php-shell-cmd.php</code>
44
*
45
* Inline Help
46
*
47
* <pre>
48
* PHP-Shell - Version 0.2.0, with readline() support
49
* (c) 2006, Jan Kneschke <jan@kneschke.de>
50
*
51
* >> use '?' to open the inline help
52
*
53
* >> ?
54
* "inline help for the PHP-shell
55
*
56
*   >> ?
57
*     print this help
58
*   >> ? <topic>
59
*     get the doccomment for a class, method, property or function
60
*   >> p <var>
61
*     execute a verbose print (if implemented)
62
*   >> quit
63
*     leave shell
64
* "
65
* >> ? PHP_Shell
66
* </pre>
67
* Alternatives
68
*
69
* - http://david.acz.org/phpa/
70
* - http://www.hping.org/phpinteractive/
71
* - the embedded interactive php-shell: $ php -a
72
*
73
* @package PHP
74
*/
75
 
76
/**
77
* PHP_Shell
78
*
79
* a interactive PHP Shell with tab-completion and history
80
* it can catch FATAL errors before executing the code
81
*
82
* Extensions are provided through three side-classes:
83
*
84
* - PHP_Shell_Commands
85
* - PHP_Shell_Options
86
* - PHP_Shell_Extensions
87
*
88
* @package PHP
89
*/
90
 
91
require_once(dirname(__FILE__)."/Shell/Commands.php");
92
require_once(dirname(__FILE__)."/Shell/Options.php"); /* for the tab-complete */
93
 
94
class PHP_Shell {
95
    /**
96
    * current code-buffer
97
    * @var string
98
    */
99
    protected $code;
100
 
101
    /**
102
    * set if readline support is enabled
103
    * @var bool
104
    */
105
    protected $have_readline;
106
 
107
    /**
108
    * current version of the class
109
    * @var string
110
    */
111
    protected $version = '0.3.1';
112
 
113
    /**
114
    *
115
    */
116
    protected $stdin;
117
 
118
    protected $code_buffer;
119
 
120
	public $has_semicolon=false;
121
 
122
    /**
123
    * init the shell and change if readline support is available
124
    */
125
    public function __construct() {
126
        $this->code = '';
127
 
128
        $this->stdin = null;
129
 
130
        $this->have_readline = function_exists('readline');
131
 
132
        if ($this->have_readline) {
133
            readline_completion_function('__shell_readline_complete');
134
        }
135
 
136
        $this->use_readline = true;
137
 
138
        $cmd = PHP_Shell_Commands::getInstance();
139
 
140
        $cmd->registerCommand('#^quit$#', $this, 'cmdQuit', 'quit', 'leaves the shell');
141
        $cmd->registerCommand('#^\?$#', $this, 'cmdHelp', '?', 'show this help');
142
        $cmd->registerCommand('#^\?\s+license$#', $this, 'cmdLicense', '? license', 'show license of the shell');
143
    }
144
 
145
 
146
    /**
147
    * parse the PHP code
148
    *
149
    * we parse before we eval() the code to
150
    * - fetch fatal errors before they come up
151
    * - know about where we have to wait for closing braces
152
    *
153
    * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise
154
    */
155
    public function parse() {
156
        ## remove empty lines
157
        if (trim($this->code) == '') return 1;
158
 
159
        $t = token_get_all('<?php '.$this->code.' ?>');
160
 
161
        $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */
162
        $need_return = 1;    /* can we prepend a return to the eval-string ? */
163
        $open_comment = 0;   /* a open multi-line comment */
164
        $eval = '';          /* code to be eval()'ed later */
165
        $braces = array();   /* to track if we need more closing braces */
166
 
167
        $methods = array();  /* to track duplicate methods in a class declaration */
168
        $ts = array();       /* tokens without whitespaces */
169
 
170
        foreach ($t as $ndx => $token) {
171
            if (is_array($token)) {
172
                $ignore = 0;
173
 
174
                switch($token[0]) {
175
                case T_WHITESPACE:
176
                case T_OPEN_TAG:
177
                case T_CLOSE_TAG:
178
                    $ignore = 1;
179
                    break;
180
                case T_FOREACH:
181
                case T_DO:
182
                case T_WHILE:
183
                case T_FOR:
184
 
185
                case T_IF:
186
                case T_RETURN:
187
 
188
                case T_CLASS:
189
                case T_FUNCTION:
190
                case T_INTERFACE:
191
 
192
                case T_PRINT:
193
                case T_ECHO:
194
 
195
                case T_COMMENT:
196
                case T_UNSET:
197
 
198
                case T_INCLUDE:
199
                case T_REQUIRE:
200
                case T_INCLUDE_ONCE:
201
                case T_REQUIRE_ONCE:
202
                case T_TRY:
203
                case T_SWITCH:
204
                case T_DEFAULT:
205
                case T_CASE:
206
                case T_BREAK:
207
                case T_DOC_COMMENT:
208
                    $need_return = 0;
209
                    break;
210
                case T_EMPTY:
211
                case T_ISSET:
212
                case T_EVAL:
213
                case T_EXIT:
214
 
215
                case T_VARIABLE:
216
                case T_STRING:
217
                case T_NEW:
218
                case T_EXTENDS:
219
                case T_IMPLEMENTS:
220
                case T_OBJECT_OPERATOR:
221
                case T_DOUBLE_COLON:
222
                case T_INSTANCEOF:
223
 
224
                case T_CATCH:
225
                case T_THROW:
226
 
227
                case T_ELSE:
228
                case T_AS:
229
                case T_LNUMBER:
230
                case T_DNUMBER:
231
                case T_CONSTANT_ENCAPSED_STRING:
232
                case T_ENCAPSED_AND_WHITESPACE:
233
                case T_CHARACTER:
234
                case T_ARRAY:
235
                case T_DOUBLE_ARROW:
236
 
237
                case T_CONST:
238
                case T_PUBLIC:
239
                case T_PROTECTED:
240
                case T_PRIVATE:
241
                case T_ABSTRACT:
242
                case T_STATIC:
243
                case T_VAR:
244
 
245
                case T_INC:
246
                case T_DEC:
247
                case T_SL:
248
                case T_SL_EQUAL:
249
                case T_SR:
250
                case T_SR_EQUAL:
251
 
252
                case T_IS_EQUAL:
253
                case T_IS_IDENTICAL:
254
                case T_IS_GREATER_OR_EQUAL:
255
                case T_IS_SMALLER_OR_EQUAL:
256
 
257
                case T_BOOLEAN_OR:
258
                case T_LOGICAL_OR:
259
                case T_BOOLEAN_AND:
260
                case T_LOGICAL_AND:
261
                case T_LOGICAL_XOR:
262
                case T_MINUS_EQUAL:
263
                case T_PLUS_EQUAL:
264
                case T_MUL_EQUAL:
265
                case T_DIV_EQUAL:
266
                case T_MOD_EQUAL:
267
                case T_XOR_EQUAL:
268
                case T_AND_EQUAL:
269
                case T_OR_EQUAL:
270
 
271
                case T_FUNC_C:
272
                case T_CLASS_C:
273
                case T_LINE:
274
                case T_FILE:
275
 
276
                case T_BOOL_CAST:
277
                case T_INT_CAST:
278
                case T_STRING_CAST:
279
 
280
                    /* just go on */
281
                    break;
282
                default:
283
                    /* debug unknown tags*/
284
                    error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1]));
285
 
286
                    break;
287
                }
288
                if (!$ignore) {
289
                    $eval .= $token[1]." ";
290
                    $ts[] = array("token" => $token[0], "value" => $token[1]);
291
                }
292
            } else {
293
                $ts[] = array("token" => $token, "value" => '');
294
 
295
                $last = count($ts) - 1;
296
 
297
                switch ($token) {
298
                case '(':
299
                    /* walk backwards through the tokens */
300
 
301
                    if ($last >= 4 &&
302
                        $ts[$last - 1]['token'] == T_STRING &&
303
                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
304
                        $ts[$last - 3]['token'] == ')' ) {
305
                        /* func()->method()
306
                        *
307
                        * we can't know what func() is return, so we can't
308
                        * say if the method() exists or not
309
                        *
310
                        */
311
                    } else if ($last >= 3 &&
312
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
313
                        $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
314
                        $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
315
                        $ts[$last - 1]['token'] == T_STRING &&
316
                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
317
                        $ts[$last - 3]['token'] == T_VARIABLE ) {
318
 
319
                        /* $object->method( */
320
 
321
                        /* catch (Exception $e) does not set $e in $GLOBALS[] */
322
                        $in_catch = 0;
323
 
324
                        foreach ($ts as $v) {
325
                            if ($v['token'] == T_CATCH) {
326
                                $in_catch = 1;
327
                            }
328
                        }
329
 
330
                        if (!$in_catch) {
331
                            /* $object has to exist and has to be a object */
332
                            $objname = $ts[$last - 3]['value'];
333
 
334
                            if (!isset($GLOBALS[ltrim($objname, '$')])) {
335
                                throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
336
                            }
337
                            $object = $GLOBALS[ltrim($objname, '$')];
338
 
339
                            if (!is_object($object)) {
340
                                throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
341
                            }
342
 
343
                            $method = $ts[$last - 1]['value'];
344
 
345
                            /* obj */
346
 
347
                            if (!method_exists($object, $method)) {
348
                                throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
349
                                    $objname, get_class($object), $method));
350
                            }
351
                        }
352
                    } else if ($last >= 3 &&
353
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
354
                        $ts[$last - 1]['token'] == T_VARIABLE &&
355
                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
356
                        $ts[$last - 3]['token'] == T_VARIABLE ) {
357
 
358
                        /* $object->$method( */
359
 
360
                        /* $object has to exist and has to be a object */
361
                        $objname = $ts[$last - 3]['value'];
362
 
363
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
364
                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
365
                        }
366
                        $object = $GLOBALS[ltrim($objname, '$')];
367
 
368
                        if (!is_object($object)) {
369
                            throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
370
                        }
371
 
372
                        $methodname = $ts[$last - 1]['value'];
373
 
374
                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
375
                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
376
                        }
377
                        $method = $GLOBALS[ltrim($methodname, '$')];
378
 
379
                        /* obj */
380
 
381
                        if (!method_exists($object, $method)) {
382
                            throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
383
                                $objname, get_class($object), $method));
384
                        }
385
 
386
                    } else if ($last >= 6 &&
387
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
388
                        $ts[$last - 1]['token'] == T_STRING &&
389
                        $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
390
                        $ts[$last - 3]['token'] == ']' &&
391
                            /* might be anything as index */
392
                        $ts[$last - 5]['token'] == '[' &&
393
                        $ts[$last - 6]['token'] == T_VARIABLE ) {
394
 
395
                        /* $object[...]->method( */
396
 
397
                        /* $object has to exist and has to be a object */
398
                        $objname = $ts[$last - 6]['value'];
399
 
400
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
401
                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
402
                        }
403
                        $array = $GLOBALS[ltrim($objname, '$')];
404
 
405
                        if (!is_array($array)) {
406
                            throw new Exception(sprintf('Variable \'%s\' is not a array', $objname));
407
                        }
408
 
409
                        $andx = $ts[$last - 4]['value'];
410
 
411
                        if (!isset($array[$andx])) {
412
                            throw new Exception(sprintf('%s[\'%s\'] is not set', $objname, $andx));
413
                        }
414
 
415
                        $object = $array[$andx];
416
 
417
                        if (!is_object($object)) {
418
                            throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
419
                        }
420
 
421
                        $method = $ts[$last - 1]['value'];
422
 
423
                        /* obj */
424
 
425
                        if (!method_exists($object, $method)) {
426
                            throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
427
                                $objname, get_class($object), $method));
428
                        }
429
 
430
                    } else if ($last >= 3 &&
431
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
432
                        $ts[$last - 1]['token'] == T_STRING &&
433
                        $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
434
                        $ts[$last - 3]['token'] == T_STRING ) {
435
 
436
                        /* Class::method() */
437
 
438
                        /* $object has to exist and has to be a object */
439
                        $classname = $ts[$last - 3]['value'];
440
 
441
                        if (!class_exists($classname)) {
442
                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
443
                        }
444
 
445
                        $method = $ts[$last - 1]['value'];
446
 
447
                        if (!in_array($method, get_class_methods($classname))) {
448
                            throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
449
                                $classname, $method));
450
                        }
451
                    } else if ($last >= 3 &&
452
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
453
                        $ts[$last - 1]['token'] == T_VARIABLE &&
454
                        $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
455
                        $ts[$last - 3]['token'] == T_STRING ) {
456
 
457
                        /* $var::method() */
458
 
459
                        /* $object has to exist and has to be a object */
460
                        $classname = $ts[$last - 3]['value'];
461
 
462
                        if (!class_exists($classname)) {
463
                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
464
                        }
465
 
466
                        $methodname = $ts[$last - 1]['value'];
467
 
468
                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
469
                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
470
                        }
471
                        $method = $GLOBALS[ltrim($methodname, '$')];
472
 
473
                        if (!in_array($method, get_class_methods($classname))) {
474
                            throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
475
                                $classname, $method));
476
                        }
477
 
478
                    } else if ($last >= 2 &&
479
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
480
                        $ts[$last - 1]['token'] == T_STRING &&
481
                        $ts[$last - 2]['token'] == T_NEW ) {
482
 
483
                        /* new Class() */
484
 
485
                        /* don't care about this in a class ... { ... } */
486
 
487
                        $classname = $ts[$last - 1]['value'];
488
 
489
                        if (!class_exists($classname)) {
490
                            throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
491
                        }
492
 
493
                        $r = new ReflectionClass($classname);
494
 
495
                        if ($r->isAbstract()) {
496
                            throw new Exception(sprintf("Can't instantiate abstract Class '%s'", $classname));
497
                        }
498
 
499
                        if (!$r->isInstantiable()) {
500
                            throw new Exception(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname));
501
                        }
502
 
503
                    } else if ($last >= 2 &&
504
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
505
                        $ts[$last - 1]['token'] == T_STRING &&
506
                        $ts[$last - 2]['token'] == T_FUNCTION ) {
507
 
508
                        /* make sure we are not a in class definition */
509
 
510
                        /* function a() */
511
 
512
                        $func = $ts[$last - 1]['value'];
513
 
514
                        if (function_exists($func)) {
515
                            throw new Exception(sprintf('Function \'%s\' is already defined', $func));
516
                        }
517
                    } else if ($last >= 4 &&
518
                        $ts[0]['token'] == T_CLASS &&
519
                        $ts[1]['token'] == T_STRING &&
520
                        $ts[$last - 1]['token'] == T_STRING &&
521
                        $ts[$last - 2]['token'] == T_FUNCTION ) {
522
 
523
                        /* make sure we are not a in class definition */
524
 
525
                        /* class a { .. function a() ... } */
526
 
527
                        $func = $ts[$last - 1]['value'];
528
                        $classname = $ts[1]['value'];
529
 
530
                        if (isset($methods[$func])) {
531
                            throw new Exception(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname));
532
                        }
533
 
534
                        $methods[$func] = 1;
535
 
536
                    } else if ($last >= 1 &&
537
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
538
                        $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
539
                        $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
540
                        $ts[$last - 1]['token'] == T_STRING ) {
541
                        /* func() */
542
                        $funcname = $ts[$last - 1]['value'];
543
 
544
                        if (!function_exists($funcname)) {
545
                            throw new Exception(sprintf("Function %s() doesn't exist", $funcname));
546
                        }
547
                    } else if ($last >= 1 &&
548
                        $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
549
                        $ts[$last - 1]['token'] == T_VARIABLE ) {
550
 
551
                        /* $object has to exist and has to be a object */
552
                        $funcname = $ts[$last - 1]['value'];
553
 
554
                        if (!isset($GLOBALS[ltrim($funcname, '$')])) {
555
                            throw new Exception(sprintf('Variable \'%s\' is not set', $funcname));
556
                        }
557
                        $func = $GLOBALS[ltrim($funcname, '$')];
558
 
559
                        if (!function_exists($func)) {
560
                            throw new Exception(sprintf("Function %s() doesn't exist", $func));
561
                        }
562
 
563
                    }
564
 
565
                    array_push($braces, $token);
566
                    break;
567
                case '{':
568
                    $need_return = 0;
569
 
570
                    if ($last >= 2 &&
571
                        $ts[$last - 1]['token'] == T_STRING &&
572
                        $ts[$last - 2]['token'] == T_CLASS ) {
573
 
574
                        /* class name { */
575
 
576
                        $classname = $ts[$last - 1]['value'];
577
 
578
                        if (class_exists($classname, false)) {
579
                            throw new Exception(sprintf("Class '%s' can't be redeclared", $classname));
580
                        }
581
                    } else if ($last >= 4 &&
582
                        $ts[$last - 1]['token'] == T_STRING &&
583
                        $ts[$last - 2]['token'] == T_EXTENDS &&
584
                        $ts[$last - 3]['token'] == T_STRING &&
585
                        $ts[$last - 4]['token'] == T_CLASS ) {
586
 
587
                        /* class classname extends classname { */
588
 
589
                        $classname = $ts[$last - 3]['value'];
590
                        $extendsname = $ts[$last - 1]['value'];
591
 
592
                        if (class_exists($classname, false)) {
593
                            throw new Exception(sprintf("Class '%s' can't be redeclared",
594
                                $classname));
595
                        }
596
                        if (!class_exists($extendsname, true)) {
597
                            throw new Exception(sprintf("Can't extend '%s' ... from not existing Class '%s'",
598
                                $classname, $extendsname));
599
                        }
600
                    } else if ($last >= 4 &&
601
                        $ts[$last - 1]['token'] == T_STRING &&
602
                        $ts[$last - 2]['token'] == T_IMPLEMENTS &&
603
                        $ts[$last - 3]['token'] == T_STRING &&
604
                        $ts[$last - 4]['token'] == T_CLASS ) {
605
 
606
                        /* class name implements interface { */
607
 
608
                        $classname = $ts[$last - 3]['value'];
609
                        $implements = $ts[$last - 1]['value'];
610
 
611
                        if (class_exists($classname, false)) {
612
                            throw new Exception(sprintf("Class '%s' can't be redeclared",
613
                                $classname));
614
                        }
615
                        if (!interface_exists($implements, false)) {
616
                            throw new Exception(sprintf("Can't implement not existing Interface '%s' for Class '%s'",
617
                                $implements, $classname));
618
                        }
619
                    }
620
 
621
                    array_push($braces, $token);
622
                    break;
623
                case '}':
624
                    $need_return = 0;
625
                case ')':
626
                    array_pop($braces);
627
                    break;
628
                case '[':
629
                    if ($ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
630
                        $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
631
                        $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
632
                        $ts[$last - 1]['token'] == T_VARIABLE) {
633
                        /* $a[] only works on array and string */
634
 
635
                        /* $object has to exist and has to be a object */
636
                        $objname = $ts[$last - 1]['value'];
637
 
638
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
639
                            throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
640
                        }
641
                        $obj = $GLOBALS[ltrim($objname, '$')];
642
 
643
                        if (is_object($obj)) {
644
                            throw new Exception(sprintf('Objects (%s) don\'t support array access operators', $objname));
645
                        }
646
                    }
647
                    break;
648
                }
649
 
650
                $eval .= $token;
651
            }
652
        }
653
 
654
        $last = count($ts) - 1;
655
        if ($last >= 2 &&
656
            $ts[$last - 0]['token'] == T_STRING &&
657
            $ts[$last - 1]['token'] == T_DOUBLE_COLON &&
658
            $ts[$last - 2]['token'] == T_STRING ) {
659
 
660
            /* Class::constant */
661
 
662
            /* $object has to exist and has to be a object */
663
            $classname = $ts[$last - 2]['value'];
664
 
665
            if (!class_exists($classname)) {
666
                throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
667
            }
668
 
669
            $constname = $ts[$last - 0]['value'];
670
 
671
            $c = new ReflectionClass($classname);
672
            if (!$c->hasConstant($constname)) {
673
                throw new Exception(sprintf("Class '%s' doesn't have a constant named '%s'",
674
                    $classname, $constname));
675
            }
676
        } else if ($last == 0 &&
677
            $ts[$last - 0]['token'] == T_VARIABLE ) {
678
 
679
            /* $var */
680
 
681
            $varname = $ts[$last - 0]['value'];
682
 
683
            if (!isset($GLOBALS[ltrim($varname, '$')])) {
684
                throw new Exception(sprintf('Variable \'%s\' is not set', $varname));
685
            }
686
        }
687
 
688
 
689
        $need_more = (count($braces) > 0) || $open_comment;
690
 
691
        if ($need_more || ';' === $token) {
692
			$need_semicolon = 0;
693
        }
694
 
695
        if ($need_return) {
696
            $eval = "return ".$eval;
697
        }
698
 
699
        /* add a traling ; if necessary */
700
        if ($need_semicolon)
701
		{
702
			$this->has_semicolon = preg_match('/;\s*$/', $eval);
703
			$eval .= ';';
704
		}
705
 
706
        if (!$need_more) {
707
            $this->code = $eval;
708
        }
709
 
710
        return $need_more;
711
    }
712
 
713
    /**
714
    * show the prompt and fetch a single line
715
    *
716
    * uses readline() if avaialbe
717
    *
718
    * @return string a input-line
719
    */
720
    public function readline() {
721
        if (empty($this->code)) print PHP_EOL;
722
 
723
        $prompt = (empty($this->code)) ? '>> ' : '.. ';
724
 
725
        if (count($this->code_buffer) > 0) {
726
            print $prompt;
727
 
728
            $line = array_shift($this->code_buffer);
729
 
730
            print $line.PHP_EOL;
731
 
732
            return $line.PHP_EOL;
733
        }
734
 
735
        if ($this->have_readline) {
736
            $l = readline($prompt);
737
 
738
            readline_add_history($l);
739
        } else {
740
            print $prompt;
741
 
742
            if (is_null($this->stdin)) {
743
                if (false === ($this->stdin = fopen("php://stdin", "r"))) {
744
                    return false;
745
                }
746
            }
747
            $l = fgets($this->stdin);
748
        }
749
        return $l;
750
    }
751
 
752
    /**
753
    * get the inline help
754
    *
755
    * @return string the inline help as string
756
    */
757
    public function cmdHelp($l) {
758
        $o = 'Inline Help:'.PHP_EOL;
759
 
760
        $cmds = PHP_Shell_Commands::getInstance()->getCommands();
761
 
762
        $help = array();
763
        foreach ($cmds as $cmd) {
764
            $help[] = sprintf('  >> %s'.PHP_EOL.'    %s'.PHP_EOL,
765
                $cmd['command'],
766
                $cmd['description']
767
            );
768
        }
769
 
770
        return var_export(implode("\n", $help), 1);
771
    }
772
 
773
    /**
774
    * get the license string
775
    *
776
    * @return string the inline help as string
777
    */
778
    public function cmdLicense($l) {
779
        $o = <<<EOF
780
(c) 2006 Jan Kneschke <jan@kneschke.de>
781
 
782
Permission is hereby granted, free of charge, to any person obtaining a copy of
783
this software and associated documentation files (the "Software"), to deal in
784
the Software without restriction, including without limitation the rights to
785
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
786
of the Software, and to permit persons to whom the Software is furnished to do
787
so, subject to the following conditions:
788
 
789
The above copyright notice and this permission notice shall be included in all
790
copies or substantial portions of the Software.
791
 
792
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
793
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
794
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
795
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
796
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
797
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
798
SOFTWARE.
799
EOF;
800
 
801
        return var_export($o, 1);
802
    }
803
 
804
    /**
805
    * handle the 'quit' command
806
    *
807
    * @return bool false to leave the input() call
808
    * @see input
809
    */
810
    protected function cmdQuit($l) {
811
        return false;
812
    }
813
 
814
    /**
815
    * handle the input line
816
    *
817
    * read the input and handle the commands of the shell
818
    *
819
    * @return bool false on 'quit' or EOF, true otherwise
820
    */
821
    public function input() {
822
        $l = $this->readline();
823
 
824
        /* got EOF ? */
825
        if (false === $l) return false;
826
 
827
        $l = trim($l);
828
 
829
        if (empty($this->code)) {
830
            $this->verbose = 0;
831
 
832
            $cmds = PHP_Shell_Commands::getInstance()->getCommands();
833
 
834
            foreach ($cmds as $cmd) {
835
                if (preg_match($cmd['regex'], $l)) {
836
                    $obj = $cmd['obj'];
837
                    $func = $cmd['method'];
838
 
839
                    if (false === ($l = $obj->$func($l))) {
840
                        ## quit
841
                        return false;
842
                    }
843
 
844
                    if (is_array($l)) {
845
                        $this->code_buffer = $l;
846
                        $l = '';
847
                    }
848
                    break;
849
                }
850
            }
851
        }
852
 
853
        $this->appendCode($l);
854
 
855
        return true;
856
    }
857
 
858
    /**
859
    * get the code-buffer
860
    *
861
    * @return string the code-buffer
862
    */
863
    public function getCode() {
864
		return $this->code;
865
        return $code;
866
    }
867
 
868
    /**
869
    * reset the code-buffer
870
    */
871
    public function resetCode() {
872
		$this->has_semicolon=false;
873
        $this->code = '';
874
    }
875
 
876
    /**
877
    * append code to the code-buffer
878
    *
879
    * @param string $code input buffer
880
    */
881
    public function appendCode($code) {
882
        if (strlen($code)) $code .= PHP_EOL;
883
 
884
        $this->code .= $code;
885
    }
886
 
887
    /**
888
    * check if readline support is enabled
889
    *
890
    * @return bool true if enabled, false otherwise
891
    */
892
    public function hasReadline() {
893
        return $this->have_readline;
894
    }
895
 
896
    /**
897
    * get version of the class
898
    *
899
    * @return string version-string
900
    */
901
    public function getVersion() {
902
        return $this->version;
903
    }
904
}
905
 
906
/**
907
* a readline completion callback
908
*
909
* @param string $str linebuffer
910
* @param integer $pos position in linebuffer
911
* @return array list of possible matches
912
*/
913
function __shell_readline_complete($str, $pos) {
914
    $in = readline_info('line_buffer');
915
 
916
    /**
917
    * parse the line-buffer backwards to see if we have a
918
    * - constant
919
    * - function
920
    * - variable
921
    */
922
 
923
    $m = array();
924
 
925
    if (preg_match('#\$([A-Za-z0-9_]+)->#', $in, $a)) {
926
        /* check for $o->... */
927
        $name = $a[1];
928
 
929
        if (isset($GLOBALS[$name]) && is_object($GLOBALS[$name])) {
930
            $c = get_class_methods($GLOBALS[$name]);
931
 
932
            foreach ($c as $v) {
933
                $m[] = $v.'(';
934
            }
935
            $c = get_class_vars(get_class($GLOBALS[$name]));
936
 
937
            foreach ($c as $k => $v) {
938
                $m[] = $k;
939
            }
940
 
941
            return $m;
942
        }
943
    } else if (preg_match('#\$([A-Za-z0-9_]+)\[([^\]]+)\]->#', $in, $a)) {
944
        /* check for $o[...]->... */
945
        $name = $a[1];
946
 
947
        if (isset($GLOBALS[$name]) &&
948
            is_array($GLOBALS[$name]) &&
949
            isset($GLOBALS[$name][$a[2]])) {
950
 
951
            $c = get_class_methods($GLOBALS[$name][$a[2]]);
952
 
953
            foreach ($c as $v) {
954
                $m[] = $v.'(';
955
            }
956
            $c = get_class_vars(get_class($GLOBALS[$name][$a[2]]));
957
 
958
            foreach ($c as $k => $v) {
959
                $m[] = $k;
960
            }
961
            return $m;
962
        }
963
 
964
    } else if (preg_match('#([A-Za-z0-9_]+)::#', $in, $a)) {
965
        /* check for Class:: */
966
        $name = $a[1];
967
 
968
        if (class_exists($name, false)) {
969
            $c = get_class_methods($name);
970
 
971
            foreach ($c as $v) {
972
                $m[] = sprintf('%s::%s(', $name, $v);
973
            }
974
 
975
            $cl = new ReflectionClass($name);
976
            $c = $cl->getConstants();
977
 
978
            foreach ($c as $k => $v) {
979
                $m[] = sprintf('%s::%s', $name, $k);
980
            }
981
 
982
            return $m;
983
        }
984
    } else if (preg_match('#\$([a-zA-Z]?[a-zA-Z0-9_]*)$#', $in)) {
985
        $m = array_keys($GLOBALS);
986
 
987
        return $m;
988
    } else if (preg_match('#new #', $in)) {
989
        $c = get_declared_classes();
990
 
991
        foreach ($c as $v) {
992
            $m[] = $v.'(';
993
        }
994
 
995
        return $m;
996
    } else if (preg_match('#^:set #', $in)) {
997
        foreach (PHP_Shell_Options::getInstance()->getOptions() as $v) {
998
            $m[] = $v;
999
        }
1000
 
1001
        return $m;
1002
    }
1003
 
1004
    $f = get_defined_functions();
1005
 
1006
    foreach ($f['internal'] as $v) {
1007
        $m[] = $v.'(';
1008
    }
1009
 
1010
    foreach ($f['user'] as $v) {
1011
        $m[] = $v.'(';
1012
    }
1013
 
1014
    $c = get_declared_classes();
1015
 
1016
    foreach ($c as $v) {
1017
        $m[] = $v.'::';
1018
    }
1019
 
1020
    $c = get_defined_constants();
1021
 
1022
    foreach ($c as $k => $v) {
1023
        $m[] = $k;
1024
    }
1025
 
1026
    /* taken from http://de3.php.net/manual/en/reserved.php */
1027
    $m[] = 'abstract';
1028
    $m[] = 'and';
1029
    $m[] = 'array(';
1030
    $m[] = 'as';
1031
    $m[] = 'break';
1032
    $m[] = 'case';
1033
    $m[] = 'catch';
1034
    $m[] = 'class';
1035
    $m[] = 'const';
1036
    $m[] = 'continue';
1037
    # $m[] = 'declare';
1038
    $m[] = 'default';
1039
    $m[] = 'die(';
1040
    $m[] = 'do';
1041
    $m[] = 'echo(';
1042
    $m[] = 'else';
1043
    $m[] = 'elseif';
1044
    $m[] = 'empty(';
1045
    # $m[] = 'enddeclare';
1046
    $m[] = 'eval(';
1047
    $m[] = 'exception';
1048
    $m[] = 'extends';
1049
    $m[] = 'exit(';
1050
    $m[] = 'extends';
1051
    $m[] = 'final';
1052
    $m[] = 'for (';
1053
    $m[] = 'foreach (';
1054
    $m[] = 'function';
1055
    $m[] = 'global';
1056
    $m[] = 'if';
1057
    $m[] = 'implements';
1058
    $m[] = 'include "';
1059
    $m[] = 'include_once "';
1060
    $m[] = 'interface';
1061
    $m[] = 'isset(';
1062
    $m[] = 'list(';
1063
    $m[] = 'new';
1064
    $m[] = 'or';
1065
    $m[] = 'print(';
1066
    $m[] = 'private';
1067
    $m[] = 'protected';
1068
    $m[] = 'public';
1069
    $m[] = 'require "';
1070
    $m[] = 'require_once "';
1071
    $m[] = 'return';
1072
    $m[] = 'static';
1073
    $m[] = 'switch (';
1074
    $m[] = 'throw';
1075
    $m[] = 'try';
1076
    $m[] = 'unset(';
1077
    # $m[] = 'use';
1078
    $m[] = 'var';
1079
    $m[] = 'while';
1080
    $m[] = 'xor';
1081
    $m[] = '__FILE__';
1082
    $m[] = '__FUNCTION__';
1083
    $m[] = '__CLASS__';
1084
    $m[] = '__LINE__';
1085
    $m[] = '__METHOD__';
1086
 
1087
    # printf("%s ... %s\n", $str, $pos);
1088
    return $m;
1089
}
1090
 
1091