Laravel's command line tool is called "Artisan," and it comes with a few powerful features out of the box. Tinker, accessed via
php artisan tinker, is arguably my favorite Artisan command. It speeds up my workflow and allows me to interact with my application in ways I would have never thought possible.
Tinker is a REPL (read-eval-print loop). A REPL gives you a prompt that allows you to interact with your application using your language's native syntax (in this case, PHP) in a command-line style. It’s commonly used for making simple database changes and testing out new ideas and workflows.
Although some of the value Tinker provides is clear at first glance, it also has loads of hidden and exciting features available out-of-the-box. Let’s walk through and take a look at some ways you can super-charge your Tinker workflow.
Behind the scenes
Before we get started, it’s important to know a little bit about how Tinker is implemented in Laravel. It's a wrapper for a PHP package called PsySH, and you can find more information about its features at its web site and the PsySH Github repo.
Since Laravel 5.4, Tinker has been pulled into Laravel as a separate Composer dependency, which you can explore here: GitHub - laravel/tinker.
Basic Tinker usage
Reading and manipulating the models in your app seems to be the most common use case for me. Commands like
Post::all() are great for exploring the contents of your database without having to create a "test" route and
dd(Post::all()). MySQL GUIs are great for direct database manipulation, but lack functionality defined inside the application, like accessors and mutators.
When writing code, I'm often unsure about the syntax of a particular library or PHP feature. Tinker makes it easy to try out bits of code much faster than executing a script and waiting for a an error, or worse, not getting one! I often find myself doing things like
collect(['boots', 'cats', 'boots'])->unique() to verify the functionality of a Laravel collection method.
A better, more interactive,
One common debugging workflow many Laravel developers use is the following: insert a
dd() (dump and die) somewhere in your code; refresh the page; repeat. This process certainly gets the job done, but it can be limiting and repetitive. One alternative solution is to set up xDebug, but Tinker provides another option if you're trying to debug code you're running from the command line (e.g. in tests):
By replacing your calls to
dd() with a special snippet of code:
eval(\Psy\sh());, we'll get a Tinker prompt that drops us right into the application at that specific point in execution. It’s kind of like being able to pause time and play with the frozen environment. See it in action:
Pro Tip: Manually typing / remembering
eval(\Psy\sh());will get old fast, so I recommend creating a snippet in your IDE. I called mine
A quick look in the docs reveal what is actually happening when we type
eval(\Psy\sh()); // Is equivalent to: extract(\Psy\Shell::debug(get_defined_vars(), $this)); // Notice `$this` is automatically bound.
You’ll notice subtle differences when accessing Tinker through
eval(\Psy\sh()) instead of
php artisan tinker. Two missing features worth noting are auto-completion, and object casting. Laravel’s object casters tell PsySh how to render
Application objects nicely.
Pro Tip: Tinker shells can accidentally run within each other and become difficult to reason about. If you try to
quit, and find yourself in another Tinker shell, simply use
ctrl+cto exit the process.
Speeding up your Laravel Dusk workflow
The nature of browser testing makes debugging issues in your tests time consuming given you have to run a lengthy browser interaction to then discover there was a problem. Fortunately, the
eval(\Psy\sh()); snippet desribed earlier performs similarly for browser tests.
eval(\Psy\sh()); in your Dusk test, you can interact with your browser test in real time. Dusk pauses the execution of your test and drops you into a Tinker shell where you can manipulate the currently open browser window. How cool is that? Take a look:
Easy access to the PHP docs
Tinker offers another command,
doc, that is a remedy for another common pattern:
google PHP function -> scroll past the W3 schools link -> click the PHP.net link.
Follow the guide here to install the PHP Docs sqlite file. Now you can use the
doc command as a quick reference for common questions like function argument order.
doc command also provides run-time documentation on objects, classes, and methods. This can saves many visits to the Laravel docs.
rtfmis an alias for the
Other commands you might not know were available in Tinker:
trace- prints a full stack trace.
history- lists the command history. Also, it has handy options for searching history (
—grep), and replaying (
—replay) the history.
whereami- prints your current execution location (file and line number).
throw-up- re-throws the last thrown exception.
exit- closes the Tinker shell.
Each command can be explained by typing:
[command] -help into the Tinker prompt. Many commands have hidden features and aliases you can discover via the
—help option. Feel free to explore!
Pro Tip: You can also execute
usestatements from Tinker:
Tailoring Tinker with custom config
PsySH supports custom configuration through a config file stored in
~/.config/psysh/config.php. However, I personally prefer storing my configuration within my project for version control purposes. Fortunately PsySH looks in the environment for a variable
PSYSH_CONFIG for a config path as well, allowing you to store one wherever you please.
I chose to store mine in the root directory of my Laravel app, next to
package.json, etc… and specify it in my
Take a look at the PsySH Configuration Docs for a list of available configuration options.
Here is a gist of my
tinker.config.php: tinker.config.php · GitHub
Including PHP files at startup
php artisan tinker command accepts an optional file path, which can be used to load helpful configurations or perform specific logic before getting inside the Tinker shell.
For example, if I create a
bootstrap-tinker.php file in my root directory I can then run
php artisan tinker bootstrap-tinker.php, and that
bootstrap-tinker.php file will be run before I’m dropped in the shell. Any variables defined in the include will be made available in the running shell.
Pain Point: Typing
App\Models\Post by hand.
App\Models\Post::create instead of being able to just write
Post::create is a grave inconvenience. Here are a few options to ease the pain:
namespace App\Modelswill allow you to write
Postwithout the full namespace. This is not ideal because every time you restart tinker you have to re-type it.
- Including a file at startup (either in a config file or a custom include) that defines a set of class aliases. For example:
class_alias(‘App\\Models\\Post’, ‘Post’);. The only issue here is that you will have to manually add aliases every time you add a new model.
- My preferred solution is to write a script in the config file that automatically registers Model aliases. It does so by looking through the
appdirectory for classes that extend
Illuminate\Database\Eloquent\Model.phpand registering aliases for them. Feel free to take a look at my tinker.config.php file to see my implementation.
- Take advantage of Tinker’s realtime / breakpoint debugging (
eval(\Psy\sh())) next time you reach for
doc array_intersectinstead of googling
- Tailor your workflow with a custom, version controlled config file.
Tinker is crazy amounts of awesome, and you deserve to know all its capabilities.