It’s 1:00 a.m., the deadline for your web application’s delivery is in 8 hours… and it’s not working. As you try to figure out what’s going on, you fill your code with var_dump() and die() everywhere to see where the bug is…
You are annoyed. Every time you want to try out a return value or variable assignment, you have to change your source code, execute your application, and see the results… In the end, you are unsure of whether or not you’ve removed all those var_dumps from the code. Is this situation familiar to you?
PsySH is a Read-Eval-Print Loop (or REPL). You may have used a REPL before via your browser’s javascript console. If you have, you know that it possesses a lot of power and can be useful while debugging your JS code.
Talking about PHP, you might have used PHP’s interactive console (php -a) before. There, you can write some code and the console will execute it as soon as you press enter:
php -a Interactive shell php > $a = 'Hello world!'; php > echo $a; Hello world! php >
Unfortunately, the interactive shell is not a REPL since it lacks the “P” (print). I had to execute an echo statement to see the contents of $a. In a true REPL, we would have seen it immediately after assigning the value to it.
You can install PsySH globally either with a composer g require, or downloading the PsySH executable:
Composer
composer g require psy/psysh:~0.1 psysh
Direct download (Linux/Mac)
wget psysh.org/psysh chmod +x psysh ./psysh
Additionally, you can have it included per project with composer as you’ll see later in this article.
Now let’s play with PsySH a little.
./psysh Psy Shell v0.1.11 (PHP 5.5.8 — cli) by Justin Hileman >>>
The main help will be your best friend. It’s what will give you all sorts of commands and their explanations:
>>> help help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ? ls List local, instance or class variables, methods and constants. Aliases: list, dir dump Dump an object or primitive. doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man show Show the code for an object, class, constant, method or property. wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf? trace Show the current call stack. buffer Show (or clear) the contents of the code input buffer. Aliases: buf clear Clear the Psy Shell screen. history Show the Psy Shell history. exit End the current session and return to caller. Aliases: quit, q
>>> help ls Usage: ls [--vars] [-c|--constants] [-f|--functions] [-k|--classes] [-I|--interfaces] [-t|--traits] [-p|--properties] [-m|--methods] [-G|--grep="..."] [-i|--insensitive] [-v|--invert] [-g|--globals] [-n|--internal] [-u|--user] [-C|-- category="..."] [-a|--all] [-l|--long] [target] Aliases: list, dir Arguments: target A target class or object to list. Options: --vars Display variables. --constants (-c) Display defined constants. --functions (-f) Display defined functions. --classes (-k) Display declared classes. --interfaces (-I) Display declared interfaces. --traits (-t) Display declared traits. --properties (-p) Display class or object properties (public properties by default). --methods (-m) Display class or object methods (public methods by default). --grep (-G) Limit to items matching the given pattern (string or regex). --insensitive (-i) Case-insensitive search (requires --grep). --invert (-v) Inverted search (requires --grep). --globals (-g) Include global variables. --internal (-n) Limit to internal functions and classes. --user (-u) Limit to user-defined constants, functions and classes. --category (-C) Limit to constants in a specific category (e.g. "date"). --all (-a) Include private and protected methods and properties. --long (-l) List in long format: includes class names and method signatures. Help: List variables, constants, classes, interfaces, traits, functions, methods, and properties. Called without options, this will return a list of variables currently in scope. If a target object is provided, list properties, constants and methods of that target. If a class, interface or trait name is passed instead, list constants and methods on that class. e.g. >>> ls >>> ls $foo >>> ls -k --grep mongo -i >>> ls -al ReflectionClass >>> ls --constants --category date >>> ls -l --functions --grep /^array_.*/ >>>
Basically, what a REPL can do is:
<span>>>> $a = 'hello'; </span><span>=> "hello" </span><span>>>></span>
Please note that if we compare PsySH against PHP’s interactive console, PsySH prints the $a value as soon as it is assigned.
A more complex example can be as follows:
php -a Interactive shell php > $a = 'Hello world!'; php > echo $a; Hello world! php >
I defined the function say() and invoked it. Those two null you see are because neither the function definition nor its execution returned a value (the function echoes the value). Additionally, while defining the function, the prompt changed from >>> to ....
Can we define a class and instantiate it?
composer g require psy/psysh:~0.1 psysh
When I instantiated Foo, the constructor returned a reference to the object. This is why PsySh printed
wget psysh.org/psysh chmod +x psysh ./psysh
If by any chance you forgot which methods the class Foo has defined, you now have the answer. Have you used a Linux OS or Mac command line interface? Then you might be familiar with the ls command. Remember the -la options?
./psysh Psy Shell v0.1.11 (PHP 5.5.8 — cli) by Justin Hileman >>>
Sweet, isn’t it?
PsySH’s true power shines when integrated with a web application, so let’s build one.
I’m going to implement a quick application to showcase the decorator design pattern. The UML class diagram of such a pattern is as follows:
Do not worry if you do not know much about UML or design patterns, understanding them is not required for this article.
Also for this project, I created a set of test cases. Those test cases can be run by phpUnit. Again, you do not have to be familiar with Unit Testing to understand this article.
The complete source code for this little application can be found at https://github.com/sitepoint-examples/PsySH
First of all, let’s define our composer.json file in order to declare a dependency on PsySH:
>>> help help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ? ls List local, instance or class variables, methods and constants. Aliases: list, dir dump Dump an object or primitive. doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man show Show the code for an object, class, constant, method or property. wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf? trace Show the current call stack. buffer Show (or clear) the contents of the code input buffer. Aliases: buf clear Clear the Psy Shell screen. history Show the Psy Shell history. exit End the current session and return to caller. Aliases: quit, q
After a composer install you should be good to go.
Please take a look at the following source code from the file public/decorator.php. It will instantiate the SimpleWindow, DecoratedWindow, and TitledWindow objects to showcase the decorator pattern:
>>> help ls Usage: ls [--vars] [-c|--constants] [-f|--functions] [-k|--classes] [-I|--interfaces] [-t|--traits] [-p|--properties] [-m|--methods] [-G|--grep="..."] [-i|--insensitive] [-v|--invert] [-g|--globals] [-n|--internal] [-u|--user] [-C|-- category="..."] [-a|--all] [-l|--long] [target] Aliases: list, dir Arguments: target A target class or object to list. Options: --vars Display variables. --constants (-c) Display defined constants. --functions (-f) Display defined functions. --classes (-k) Display declared classes. --interfaces (-I) Display declared interfaces. --traits (-t) Display declared traits. --properties (-p) Display class or object properties (public properties by default). --methods (-m) Display class or object methods (public methods by default). --grep (-G) Limit to items matching the given pattern (string or regex). --insensitive (-i) Case-insensitive search (requires --grep). --invert (-v) Inverted search (requires --grep). --globals (-g) Include global variables. --internal (-n) Limit to internal functions and classes. --user (-u) Limit to user-defined constants, functions and classes. --category (-C) Limit to constants in a specific category (e.g. "date"). --all (-a) Include private and protected methods and properties. --long (-l) List in long format: includes class names and method signatures. Help: List variables, constants, classes, interfaces, traits, functions, methods, and properties. Called without options, this will return a list of variables currently in scope. If a target object is provided, list properties, constants and methods of that target. If a class, interface or trait name is passed instead, list constants and methods on that class. e.g. >>> ls >>> ls $foo >>> ls -k --grep mongo -i >>> ls -al ReflectionClass >>> ls --constants --category date >>> ls -l --functions --grep /^array_.*/ >>>
We can execute the code via PHP’s CLI (command line interface) or through a webserver if one is configured. We can use PHP’s internal web server too.
The execution of the above code through the command line interface will look like this:
<span>>>> $a = 'hello'; </span><span>=> "hello" </span><span>>>></span>
How can we interact with PsySH? Just add PsyShell::debug(get_defined_vars()); anywhere on the code where you want to debug your application, typically where you would insert a var_dump() statement:
>>> function say($a) { ... echo $a; ... } => null >>> say('hello'); hello => null >>>
After saving the file, we will get the following output:
>>> class Foo ... { ... protected $a; ... ... public function setA($a) { ... $this->a = $a; ... } ... ... public function getA() { ... return $this->a; ... } ... } => null >>> $foo = new Foo(); => <Foo #000000001dce50dd000000002dda326e> {} >>> $foo->setA('hello'); => null >>> $foo->getA(); => "hello" >>>
The script’s execution will be suspended, and we now have PsySH’s prompt to play with. I am passing get_defined_vars() as a parameter to PsyShell::debug() so I have access to all defined variables inside the shell:
>>> ls $foo Class Methods: getA, setA >>>
Let’s examine the $window variable:
php -a Interactive shell php > $a = 'Hello world!'; php > echo $a; Hello world! php >
Something nice about having PsySH inside an application is that we can examine the source code of an instantiated object.
composer g require psy/psysh:~0.1 psysh
So, $window is an instance of SimpleWindow, which implements the Window interface… I wonder what the source code for the Window interface looks like…
wget psysh.org/psysh chmod +x psysh ./psysh
Why do SimpleWindow and DecoratedWindow have the same output? Let’s examine the $decoratedWindow object.
./psysh Psy Shell v0.1.11 (PHP 5.5.8 — cli) by Justin Hileman >>>
This object is “heavier” than the SimpleWindow one, so the source code might be long… Let’s see the source code for the render() method only:
>>> help help Show a list of commands. Type `help [foo]` for information about [foo]. Aliases: ? ls List local, instance or class variables, methods and constants. Aliases: list, dir dump Dump an object or primitive. doc Read the documentation for an object, class, constant, method or property. Aliases: rtfm, man show Show the code for an object, class, constant, method or property. wtf Show the backtrace of the most recent exception. Aliases: last-exception, wtf? trace Show the current call stack. buffer Show (or clear) the contents of the code input buffer. Aliases: buf clear Clear the Psy Shell screen. history Show the Psy Shell history. exit End the current session and return to caller. Aliases: quit, q
The getWindowReference() method is invoked, and then it returns the result from the render() method. Let’s check the getWindowReference() source:
>>> help ls Usage: ls [--vars] [-c|--constants] [-f|--functions] [-k|--classes] [-I|--interfaces] [-t|--traits] [-p|--properties] [-m|--methods] [-G|--grep="..."] [-i|--insensitive] [-v|--invert] [-g|--globals] [-n|--internal] [-u|--user] [-C|-- category="..."] [-a|--all] [-l|--long] [target] Aliases: list, dir Arguments: target A target class or object to list. Options: --vars Display variables. --constants (-c) Display defined constants. --functions (-f) Display defined functions. --classes (-k) Display declared classes. --interfaces (-I) Display declared interfaces. --traits (-t) Display declared traits. --properties (-p) Display class or object properties (public properties by default). --methods (-m) Display class or object methods (public methods by default). --grep (-G) Limit to items matching the given pattern (string or regex). --insensitive (-i) Case-insensitive search (requires --grep). --invert (-v) Inverted search (requires --grep). --globals (-g) Include global variables. --internal (-n) Limit to internal functions and classes. --user (-u) Limit to user-defined constants, functions and classes. --category (-C) Limit to constants in a specific category (e.g. "date"). --all (-a) Include private and protected methods and properties. --long (-l) List in long format: includes class names and method signatures. Help: List variables, constants, classes, interfaces, traits, functions, methods, and properties. Called without options, this will return a list of variables currently in scope. If a target object is provided, list properties, constants and methods of that target. If a class, interface or trait name is passed instead, list constants and methods on that class. e.g. >>> ls >>> ls $foo >>> ls -k --grep mongo -i >>> ls -al ReflectionClass >>> ls --constants --category date >>> ls -l --functions --grep /^array_.*/ >>>
This method is returning the object’s windowReference property, and as we saw from the ls -al command above, it is an instance of AcmePatternsDecoratorSimpleWindow. Of course, we could have just looked at how DecoratedWindow::__construct() works, but this is another way we can check.
Unfortunately, debugging through a webserver like Apache is not supported. However, we can debug our application using PHP’s embedded server:
<span>>>> $a = 'hello'; </span><span>=> "hello" </span><span>>>></span>
The Development Server is now listening for connections on port 8080, so as soon as we request the decorator.php file through this web server (https://localhost:8080/decorator.php), we should see the following:
>>> function say($a) { ... echo $a; ... } => null >>> say('hello'); hello => null >>>
We can begin playing with PsySH just as we did with the CLI
>>> class Foo ... { ... protected $a; ... ... public function setA($a) { ... $this->a = $a; ... } ... ... public function getA() { ... return $this->a; ... } ... } => null >>> $foo = new Foo(); => <Foo #000000001dce50dd000000002dda326e> {} >>> $foo->setA('hello'); => null >>> $foo->getA(); => "hello" >>>
As a good developer, you should write unit tests for your code as proof that it is working as expected. In the project’s files you will find the tests folder, and if you have phpUnit installed, you can run the tests inside it.
>>> ls $foo Class Methods: getA, setA >>>
Even though the code appears to run flawlessly, a test is failing. We can examine further by running just the failing test:
>>> ls -la $foo Class Properties: $a "hello" Class Methods: getA public function getA() setA public function setA($a)
We have the test, file and line where the error is being generated. Let’s take a look at TitledWindowTest.php
{ "name": "example/psysh", "authors": [ { "name": "John Doe", "email": "john@doe.tst" } ], "require": { "psy/psysh": "~0.1" }, "autoload": { "psr-4": {"Acme\": "src/"} } }
If you are unfamiliar with phpUnit, do not pay too much attention to the code. In a nutshell, I’m setting up everything to test the TitledWindow::addTitle() method, and expecting to receive a non empty value.
So, how do we use PsySh to check what is going on? Just add the Shell::debug() method as we did previously.
<span><span><?php </span></span><span><span>chdir(dirname(__DIR__)); </span></span><span> </span><span><span>require_once('vendor/autoload.php'); </span></span><span> </span><span><span>use Acme<span>\Patterns\Decorator\SimpleWindow</span>; </span></span><span><span>use Acme<span>\Patterns\Decorator\DecoratedWindow</span>; </span></span><span><span>use Acme<span>\Patterns\Decorator\TitledWindow</span>; </span></span><span> </span><span><span>echo PHP_EOL . 'Simple Window' . PHP_EOL; </span></span><span> </span><span><span>$window = new SimpleWindow(); </span></span><span> </span><span><span>echo $window->render(); </span></span><span> </span><span><span>echo PHP_EOL . 'Decorated Simple Window' . PHP_EOL; </span></span><span> </span><span><span>$decoratedWindow = new DecoratedWindow($window); </span></span><span> </span><span><span>echo $decoratedWindow->render(); </span></span><span> </span><span><span>echo PHP_EOL . 'Titled Simple Window' . PHP_EOL; </span></span><span> </span><span><span>$titledWindow = new TitledWindow($window); </span></span><span> </span><span><span>echo $titledWindow->render();</span></span>
We are ready to rock!
php public/decorator.php Simple Window +-------------+ | | | | | | | | | | +-------------+ Decorated Simple Window +-------------+ | | | | | | | | | | +-------------+ Titled Simple Window +-------------+ |Title | +-------------+ | | | | | | | | | | +-------------+
So in $rs we should have a string; let’s see what we really have.
<span><span><?php </span></span><span><span>chdir(dirname(__DIR__)); </span></span><span> </span><span><span>require_once('vendor/autoload.php'); </span></span><span> </span><span><span>//... a lot of code here </span></span><span> </span><span><span>$titledWindow = new TitledWindow($window); </span></span><span> </span><span><span>echo $titledWindow->render(); </span></span><span> </span><span><span><span>\Psy\Shell</span>::debug(get_defined_vars()); //we want to debug our application here!</span></span>
Null value, no wonder the test failed… Let’s check the source code of TitledWindow::addTitle(). If we perform an ls command, we can see that we have that object’s method available through the $titledWindow object.
php -a Interactive shell php > $a = 'Hello world!'; php > echo $a; Hello world! php >
There is the bug. The method is echoing the value instead of returning it. Even though the application seems to work right, through unit testing and PsySH we discovered a defect and we can now fix it.
This article was not meant to be exhaustive in showcasing all the potential PsySH has. There are other cool features (like ‘doc’) that you should try out. PsySH alone may not be very useful, but if combined with other tools and your clever debugging powers, it can prove to be a valuable asset.
PsySH is a runtime developer console, interactive debugger, and Read-Eval-Print Loop (REPL) for PHP. It provides an interactive command-line interface where you can execute PHP code and see the output immediately. PsySH is particularly useful for debugging as it allows you to step through your code, inspect variables, and interactively test changes. It’s like having a conversation with your code, which can lead to better understanding and quicker bug resolution.
PsySH can be installed globally using Composer, a dependency management tool for PHP. You can install it by running the command composer global require psy/psysh. After installation, you can start PsySH by simply typing psysh in your terminal. Make sure to include the global Composer binaries in your PATH so your system can locate the PsySH executable.
To debug your PHP code using PsySH, you can insert Psysh(); at any point in your code where you want to start an interactive debugging session. When your code execution reaches this point, PsySH will open an interactive shell, allowing you to inspect variables, execute code, and step through your code execution.
Yes, PsySH can be very useful for unit testing in PHP. You can use PsySH to interactively debug your tests, inspecting variables and state at any point during the test execution. This can be particularly useful for understanding why a test is failing.
PsySH allows you to customize its configuration by creating a .psysh.php file in your home directory. In this file, you can set configuration options such as default includes, error level, and command history size. You can also add custom commands or cleaners.
PsySH comes with many advanced features that can help you debug your PHP code more effectively. These include code execution with runtime, automatic semicolon insertion, namespace support, readline support, exception handling, and more. PsySH also supports tab completion for variables, functions, classes, and even PHP built-in keywords.
PsySH has a robust error and exception handling mechanism. When an error or exception occurs, PsySH will display a detailed stack trace, helping you to understand exactly where and why the error occurred. You can also use the wtf command to display the last exception stack trace at any time.
Yes, PsySH works well with most PHP frameworks, including Laravel, Symfony, and Zend Framework. Some frameworks, like Laravel, even include PsySH out of the box for their tinker command.
PsySH is an open-source project hosted on GitHub. You can contribute to the project by reporting bugs, suggesting features, or submitting pull requests. Before contributing, make sure to read the project’s contributing guidelines.
The official PsySH website and its GitHub repository are the best places to find resources about PsySH. They include detailed documentation, usage examples, and a list of available commands. You can also find tutorials and articles on various PHP and developer blogs.
The above is the detailed content of Interactive PHP Debugging with PsySH. For more information, please follow other related articles on the PHP Chinese website!