Completed
Push — prado-3.3 ( e90646...0b76d5 )
by Fabio
23:37 queued 03:01
created

PHP_Shell::hasReadline()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4
/*
5
(c) 2006 Jan Kneschke <[email protected]>
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 <[email protected]>
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;
0 ignored issues
show
Bug introduced by
The property use_readline does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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() {
0 ignored issues
show
Coding Style introduced by
parse uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
184
185
                case T_IF:
186
                case T_RETURN:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
187
188
                case T_CLASS:
189
                case T_FUNCTION:
190
                case T_INTERFACE:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
191
192
                case T_PRINT:
193
                case T_ECHO:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
194
195
                case T_COMMENT:
196
                case T_UNSET:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
223
224
                case T_CATCH:
225
                case T_THROW:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
270
271
                case T_FUNC_C:
272
                case T_CLASS_C:
273
                case T_LINE:
274
                case T_FILE:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
275
276
                case T_BOOL_CAST:
277
                case T_INT_CAST:
278
                case T_STRING_CAST:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
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 &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
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 View Code Duplication
                            if (!isset($GLOBALS[ltrim($objname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                            if (!method_exists($object, $method)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
375
                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
376
                        }
377
                        $method = $GLOBALS[ltrim($methodname, '$')];
378
379
                        /* obj */
380
381 View Code Duplication
                        if (!method_exists($object, $method)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!method_exists($object, $method)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!in_array($method, get_class_methods($classname))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($methodname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
469
                            throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
470
                        }
471
                        $method = $GLOBALS[ltrim($methodname, '$')];
472
473 View Code Duplication
                        if (!in_array($method, get_class_methods($classname))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($funcname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 '}':
0 ignored issues
show
Coding Style introduced by
There must be a comment when fall-through is intentional in a non-empty case body
Loading history...
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 View Code Duplication
                        if (!isset($GLOBALS[ltrim($objname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
            if (!isset($GLOBALS[ltrim($varname, '$')])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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) {
0 ignored issues
show
Bug introduced by
The variable $token seems to be defined by a foreach iteration on line 170. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
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);
0 ignored issues
show
Documentation Bug introduced by
The property $has_semicolon was declared of type boolean, but preg_match('/;\\s*$/', $eval) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
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;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PHP_Shell::readline of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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;
0 ignored issues
show
Unused Code introduced by
$o is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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 <[email protected]>
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;
0 ignored issues
show
Bug introduced by
The property verbose does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
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;
0 ignored issues
show
Unused Code introduced by
return $code; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
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) {
0 ignored issues
show
Coding Style introduced by
__shell_readline_complete uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
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
1092