REPL (Read-Eval-Print-Loop)
Yalla includes a powerful interactive REPL for PHP development, testing, and debugging.
Starting the REPL
# Basic usage
./cli repl
# With custom configuration
./cli repl --config=repl.config.php
# Without colors
./cli repl --no-colors
# Without history
./cli repl --no-historyBasic Usage
Evaluating Expressions
> 2 + 2
4
> "Hello " . "World"
"Hello World"
> [1, 2, 3]
[1, 2, 3]
> range(1, 5)
[1, 2, 3, 4, 5]Working with Variables
> $name = "Alice"
"Alice"
> $age = 30
30
> echo "Name: $name, Age: $age"
Name: Alice, Age: 30
> $user = ['name' => $name, 'age' => $age]
['name' => "Alice", 'age' => 30]Using PHP Functions
> strlen("Hello World")
11
> array_map(fn($x) => $x * 2, [1, 2, 3])
[2, 4, 6]
> json_encode(['status' => 'success'])
'{"status":"success"}'
> date('Y-m-d H:i:s')
"2025-01-14 10:30:45"REPL Commands
All REPL commands start with a colon (:):
Basic Commands
:help- Show all available commands:exitor:quit- Exit the REPL:clear- Clear the screen:vars- Show all defined variables:imports- Show imported classes:history- Show command history
Display Modes
Switch between different output formats:
> :mode
Current display mode: compact
> :mode verbose
Display mode changed to: verbose
> :mode json
Display mode changed to: json
> :mode dump
Display mode changed to: dump
> :mode compact
Display mode changed to: compactDisplay Modes Explained
Compact Mode (Default)
Clean, colorized output for everyday use:
> ['id' => 1, 'name' => 'Alice']
['id' => 1, 'name' => "Alice"]
> "Hello World"
"Hello World"Verbose Mode
Detailed information about values:
> :mode verbose
> $user = new stdClass
═══ Object Details ═══
Class: stdClass
Properties: (none)
Public Methods: (none)
> ['a' => 1, 'b' => 2]
═══ Array Details ═══
Type: Associative Array
Count: 2 items
Keys: a, b
Values:
[a] => 1
[b] => 2JSON Mode
Perfect for API data and configuration:
> :mode json
> ['status' => 'success', 'data' => ['id' => 1]]
{
"status": "success",
"data": {
"id": 1
}
}Dump Mode
Traditional PHP var_dump output:
> :mode dump
> "test"
string(4) "test"
> [1, 2, 3]
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}Working with Classes
Creating Objects
> class User {
public $name;
public $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
}
> $user = new User("Alice", "alice@example.com")
User {
name: "Alice"
email: "alice@example.com"
}Using Namespaced Classes
> use DateTime
> $date = new DateTime()
DateTime object
> use DateInterval
> $interval = new DateInterval('P1D')
DateInterval objectConfiguration
Creating a Configuration File
Create repl.config.php:
<?php
return [
// Class shortcuts
'shortcuts' => [
'User' => '\App\Models\User',
'DB' => '\App\Database\DB',
'Carbon' => '\Carbon\Carbon',
],
// Auto-imports
'imports' => [
['class' => '\DateTime', 'alias' => 'DateTime'],
['class' => '\DateTimeZone', 'alias' => 'DateTimeZone'],
],
// Display settings
'display' => [
'prompt' => '>>> ', // Custom prompt
'performance' => true, // Show execution time
'mode' => 'compact', // Default display mode
'colors' => true, // Enable colors
'max_depth' => 3, // Max nesting depth
'truncate' => 1000, // Truncate long strings
],
// History settings
'history' => [
'enabled' => true,
'file' => $_ENV['HOME'] . '/.yalla_history',
'max_entries' => 1000,
'ignore_duplicates' => true,
],
// Autocomplete settings
'autocomplete' => [
'enabled' => true,
'max_suggestions' => 10,
],
];Using the Configuration
./cli repl --config=repl.config.phpNow you can use shortcuts:
> $user = new User // Expands to \App\Models\User
> Carbon::now() // Expands to \Carbon\Carbon::now()Creating REPL Extensions
Basic Extension
<?php
use Yalla\Repl\ReplExtension;
use Yalla\Repl\ReplContext;
class DatabaseExtension implements ReplExtension
{
private $connection;
public function register(ReplContext $context): void
{
// Add custom commands
$context->addCommand('db:tables', function($args, $output) {
$tables = $this->getTables();
$output->table(['Table Name'], array_map(fn($t) => [$t], $tables));
});
$context->addCommand('db:query', function($args, $output) {
$result = $this->query($args);
$output->writeln(json_encode($result, JSON_PRETTY_PRINT));
});
// Add shortcuts
$context->addShortcut('DB', '\App\Database');
// Add custom variables
$context->setVariable('db', $this->connection);
}
public function boot(): void
{
// Initialize database connection
$this->connection = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
}
public function getName(): string
{
return 'Database Extension';
}
public function getVersion(): string
{
return '1.0.0';
}
public function getDescription(): string
{
return 'Adds database interaction to the REPL';
}
private function getTables(): array
{
// Implementation...
return ['users', 'posts', 'comments'];
}
private function query(string $sql)
{
// Implementation...
return [];
}
}Registering Extensions
use Yalla\Repl\ReplCommand;
$replCommand = new ReplCommand();
$replCommand->registerExtension(new DatabaseExtension());Advanced Features
Custom Display Formatters
// In your extension
$context->addFormatter('App\Models\User', function($user, $output) {
$output->writeln("User: {$user->name} ({$user->email})");
$output->writeln("Created: {$user->created_at}");
});Middleware
Process input and output:
// Input middleware
$context->addMiddleware('input', function($input) {
// Expand shortcuts
return str_replace('$$', '$this->', $input);
});
// Output middleware
$context->addMiddleware('output', function($output) {
// Format output
if (is_array($output) && count($output) > 10) {
return array_slice($output, 0, 10) + ['...' => 'truncated'];
}
return $output;
});Custom Evaluators
$context->addEvaluator(function($input) {
// Handle SQL queries
if (str_starts_with($input, 'SQL:')) {
$sql = substr($input, 4);
return $this->database->query($sql);
}
return null; // Let default evaluator handle it
});Practical Examples
Working with Databases
> $pdo = new PDO('sqlite::memory:')
> $pdo->exec('CREATE TABLE users (id INTEGER, name TEXT)')
> $pdo->exec("INSERT INTO users VALUES (1, 'Alice')")
> $stmt = $pdo->query('SELECT * FROM users')
> $stmt->fetchAll()
[
['id' => 1, 'name' => 'Alice']
]Testing Code Snippets
> function fibonacci($n) {
if ($n <= 1) return $n;
return fibonacci($n - 1) + fibonacci($n - 2);
}
> fibonacci(10)
55
> array_map('fibonacci', range(0, 10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]Working with JSON
> $json = '{"name": "Alice", "age": 30}'
> $data = json_decode($json, true)
['name' => 'Alice', 'age' => 30]
> $data['age'] += 1
> json_encode($data)
'{"name":"Alice","age":31}'File Operations
> file_put_contents('test.txt', 'Hello World')
11
> file_get_contents('test.txt')
"Hello World"
> unlink('test.txt')
trueTips and Tricks
1. Use Tab Completion
The REPL supports tab completion for:
- PHP built-in functions
- Defined variables (starting with
$) - Class shortcuts
- REPL commands (starting with
:)
2. Multi-line Input
For multi-line code, the REPL automatically detects incomplete statements:
> function hello($name) {
return "Hello, $name!";
}3. Command History
Use up/down arrows to navigate through command history.
4. Quick Testing
Test your command logic quickly:
> $output = new Yalla\Output\Output
> $output->success('It works!')
It works!
> $output->table(['Name', 'Age'], [['Alice', 30], ['Bob', 25]])
│ Name │ Age │
├───────┼─────┤
│ Alice │ 30 │
│ Bob │ 25 │5. Performance Testing
With performance display enabled:
> range(1, 1000000)
[Execution time: 0.0234s] [Memory: 32.5 MB]Troubleshooting
Common Issues
Issue: Colors not displaying Solution: Check terminal support or use --no-colors
Issue: History not saving Solution: Check write permissions for history file
Issue: Autocomplete not working Solution: Ensure readline extension is installed
Issue: Class not found Solution: Use fully qualified names or configure shortcuts
Security Considerations
The REPL executes PHP code with full access to your system. Be careful when:
- Running code from untrusted sources
- Executing system commands
- Working with sensitive data
- Using in production environments
Always use the REPL in a controlled environment and never expose it to untrusted users.