alexpw / boris
Requires
- php: >=5.3.0
- hoa/console: dev-master
- hoa/core: dev-master
- hoa/stream: dev-master
- hoa/string: dev-master
Suggests
- ext-pcntl: Optional Process Control extension depencency
- ext-posix: Optional POSIX extension
This package is not auto-updated.
Last update: 2024-09-28 16:25:51 UTC
README
这是一个分支,请参阅并考虑原始版本。此分支完全功能,但未经实战测试。本README的其余部分已修改,以准确反映此分支。
与原始版本的不同之处
- 重新定义现有函数和(基本)类,以便快速进行代码实验。
- 当运行runkit扩展时可用(可选)。
- 将PHP的readline替换为基于Hoa Console的自定义版本
- 这提供了对UX的完全控制,但重新发明基本功能时存在许多潜在的错误。
- 自动补全,感谢joddie!
- 向下移植以支持PHP 5.3
- “宏”支持,允许在评估之前转换输入。
- 支持PHP的“use”语句,仅是一个宏。
- 多行历史记录。
简介
PHP的REPL(读取-评估-打印循环)。
公告:我正在寻找一两个额外的合作者,他们可以提交代码。如果你积极参与开源项目,并在GitHub上有个人资料供审查,请在Twitter上给我发消息(@d11wtq)以表示你的兴趣。只有活跃的GitHub项目经验丰富的开发者。
Python有一个。Ruby有一个。Clojure有一个。现在PHP也有了。Boris是PHP缺失的REPL(读取-评估-打印循环),允许开发者在终端以交互式方式实验PHP代码。如果你犯了一个错误,没关系,Boris会报告错误并继续运行。
你输入到Boris中的所有内容都会被评估并检查结果,这样你可以了解正在发生的事情。状态在输入之间保持不变,允许你逐步构建解决某个问题的解决方案。
为什么?
我发现PHP缺少真正的REPL令人沮丧,并且无法找到现有的完整实现。
Facebook的phpsh是用Python编写的,已被放弃,并且最新提交的大多数功能列表对我都不适用。
Ieure的PHP_Repl已有5年历史。它有一些很好的功能,但遇到致命错误时终止,这是一个破坏性的因素。
安装
1. 通过packagist
与composer一起使用。
2. 直接从此仓库
如果你想保持最新的更新,这是一个不错的选择,但(alexpw)我可能会将不稳定代码提交到master,至少直到有人开始使用这个分支。
git clone git://github.com/alexpw/boris.git
cd boris
./bin/boris
3. 创建自己的phar
您还可以使用Box创建PHAR文件
box build
这将创建一个boris.phar
文件。请随意将其移动到您的bin目录
chmod +x boris.phar
mv boris.phar /usr/local/bin/boris
专业技巧
将boris添加到您的$PATH中,以便轻松访问。
用法
当Boris启动时,您将处于php>
提示符。在此提示符处输入的PHP代码将被评估。如果一个表达式跨越多行,Boris将收集输入,然后在表达式完成后评估它。如果你犯了一个错误,请按CTRL-C清除多行输入缓冲区。
php> $x = 1;
// 1
php> $y = 2;
// 2
php> "x + y = " . ($x + $y);
// "x + y = 3"
php> exit
你还可以使用CTRL-D退出REPL。
取消长时间运行的操作
长时间运行的操作,例如无限循环,可以在操作运行时使用 CTRL-C 在任何时间取消,而无需退出 REPL。
php> for ($i = 0; ; ++$i) {
*> if ($i % 2 == 0) printf("Tick\n");
*> else printf("Tock\n");
*> sleep(1);
*> }
Tick
Tock
Tick
Tock
Tick
Tock
Tick
^CCancelling...
php>
在加载您的应用程序后使用 Boris
您还可以将 Boris 作为更大项目的一部分使用(例如,与您的应用程序环境加载)。
require_once 'lib/autoload.php';
$boris = new \Boris\Boris('myapp> ');
$boris->start();
构造函数参数是可选的,可以更改提示。
如果您想将局部变量直接传递给 Boris(例如,您的应用程序的一部分),您也可以这样做(感谢 @dhotston)
$boris = new \Boris\Boris('myapp> ');
$boris->setLocal(array('appContext' => $appContext));
$boris->start();
在上面的示例中,$appContext 将在 REPL 中存在。
使用启动钩子
可以向 Boris 添加回调,在它开始循环之前在 REPL 中执行。可以添加任意数量的钩子,并且它们将按顺序执行。钩子设置的任何变量或导出的变量将可以从 REPL 内部访问,从而可以访问后续运行的钩子。
有两种方式指定钩子:作为要评估的任意 PHP 字符串,或作为闭包提供的回调。两种方法都可以让您访问作用域,尽管您需要使用回调方法进行更多的工作。
// this will simply be evaluated in the REPL scope
// the variables $foo and $bar will be visible inside the REPL
$boris->onStart('$foo = 42; $bar = 2; echo "Hello Boris!\n";');
// this will be passed the REPL and it's scope as arguments
// any changes to the scope can be expressed by invoking the usual
// methods on the REPL
$boris->onStart(function($worker, $scope){
extract($scope); // we could also just access specific vars by key
echo '$foo * $bar = ' . ($foo * $bar) . "\n";
$worker->setLocal('name', 'Chris');
});
在上面的示例中,我们添加了两个钩子。第一个只是传递给 eval()
并在作用域中留下 $foo
和 $bar
。第二个使用回调样式,并从 $scope
参数中读取其变量,然后使用 setLocal()
将变量设置到 REPL 中。
用户配置文件
如果您有您在 Boris 启动时总是想做的事情,例如加载有用的实用函数、更改提示或设置局部变量,您可以创建一个 ~/.borisrc.php 文件,该文件将在 Boris 启动时加载(也支持 ~/.borisrc,但您将失去语法高亮)。
此文件的内容只是任意 PHP 代码。在此文件中,您不在 REPL 内部,但您可以访问 $boris
,这是 REPL 对象。以下是一个设置提示的示例 ~/.borisrc.php。
<?php
/* File: ~/.borisrc.php */
$boris->setPrompt('prompt> ');
Boris 还会在您的当前工作目录下查找此文件。如果在两个位置都找到该文件,则默认情况下将加载它们(这不是在代码级别可定制的)。如果您需要在 REPL 中执行代码,请使用上面记录的钩子。
感谢 @filp 提供此功能!
自定义输出
输入每个表达式后,Boris 会通过检查器传递它以获得对调试有用的表示。默认情况下,它对值中的数据类型进行了一些很好的高亮显示,以便更容易阅读,但您可以更改此行为。
任何具有 inspect($variable)
方法的对象都可以用于此目的。
$boris->setInspector(new BlinkInspector());
Boris 默认提供了三种选择
- \Boris\ColoredInspector,它执行数据类型高亮显示,是默认选项
- \Boris\DumpInspector,它使用简单的但有效的 var_dump()
- \Boris\ExportInspector,它使用 var_export()
请注意,您也可以在 REPL 内部更改此设置
php> $this->setInspector(new \Boris\ExportInspector());
// NULL
php> "Test";
// 'Test'
为了进一步自定义 \Boris\ColoredInspector
内部的对象输出,您可以通过重写 objectVars($value)
方法进行子类化
class MyInspector extends \Boris\ColoredInspector {
public function objectVars($value) {
if ($value instanceof User) {
return array('user_id' => $value->getUserId());
}
return parent::objectVars($value);
}
}
这覆盖了默认行为,即简单地调用 get_object_vars()
在对象上,允许您显示可能被魔法方法或属性可见性隐藏的属性。
Boris 在我的应用程序中运行时不显示异常或错误?
鲍里斯尊重您的环境。如果您的应用程序安装了错误处理器,它们将掩盖错误。同样,如果安装了异常处理器,您将看不到堆栈跟踪(除非您的异常处理器显示它)。
由于鲍里斯在您可以在控制台看到错误时非常有用,所以最好的做法是在应用程序在鲍里斯内部运行时禁用任何异常/错误处理器。
PHP的交互式模式怎么办?
PHP的交互式模式不会打印表达式评估的结果,更重要的是,如果您输入了产生致命错误的内容(例如调用不存在的函数/方法或未捕获的异常),它将退出。鲍里斯被设计成像其他REPL一样健壮,这样您就可以在知道可能会出错的情况下进行实验,而不会失去一切。
架构概述
本README的这一部分仅适用于那些足够好奇以至于想阅读代码的人。鲍里斯与其他PHP REPL有很大不同,因为它以特殊的方式处理致命错误(不是异常,而是致命错误)。
鲍里斯只能在POSIX系统(Linux和Mac OS)上运行。这主要是因为它依赖于分叉的能力,但也因为它的信号处理非常多。
鲍里斯由两部分组成
- 一个REPL工作进程,它接收要评估和打印的表达式
- 一个readline客户端,它简单地接收您的输入,将其发送到工作进程,然后循环
如果PHP中的所有错误都是异常,构建REPL将很简单。但这并不是这种情况。一些PHP错误确实是致命的,无法捕获。为了防止此类致命错误杀死REPL,工作进程看起来像这样
for(;;) {
$input = accept_some_input();
if (fork_child()) {
wait_for_child();
} else { // inside child
var_dump(eval($input));
kill_parent();
}
}
子进程在具有所有当前变量和资源的情况下进行分叉。它评估输入,然后杀死父进程,然后循环在子进程中继续,等待下一个输入。
当子进程评估输入时,父进程等待。父进程预期最坏的情况——子进程会异常死亡——此时父进程将继续等待输入,而不会终止。状态保持不变。
每个表达式评估后,工作进程将状态码为0(继续运行)或1(终止)的报告回主进程。
鲍里斯的主进程(readline)非常简单直接。它接收您的输入,对其进行(非常)浅层解析,以确定是否需要等待进一步输入,或者评估它已接收的输入(一次一条语句)。如果工作进程报告状态码为1,则进程终止,否则进入循环的下一个迭代。
它会在...上工作吗?
鲍里斯依赖于以下PHP功能
并且可以选择使用
由于它依赖于POSIX功能(代码几乎完全依赖于POSIX),所以没有机会在Windows上运行。
通过pecl安装runkit
尽管runkit得到了维护,但它已经多年没有发布到pecl仓库了。所以,您最好从源代码安装它。幸运的是,pecl可以为您做到这一点。
- 下载最新的源代码
- 提取并进入。
- sudo pecl install package.xml
- 如果您,像我一样,收到缺少测试文件的投诉
- 打开package.xml并删除有问题的行。
- 重试安装(#3)。
- 确保您的php.ini包含extension=runkit.so,并且php -m列出了模块。
现在,当您尝试重新定义已存在的函数或基本类时,它将成功,而不是显示致命错误。
版权 & 许可
鲍里斯由Chris Corbyn (@d11wtq)编写和维护。您可以使用代码如您所愿。有关详细信息,请参阅LICENSE文件。