unicorn-fail / php-option
一个高度可扩展的 [phpoption/phpoption] 替代品,支持 `TypedOption`。
README
一个高度可扩展的 [phpoption/phpoption](https://packagist.org.cn/packages/phpoption/phpoption) 替代品,支持
TypedOption
。
Option
用于在根据参数或其他运行时因素有时返回值(通常是对象)和有时不返回值(通常是 null)的情况下。
通常情况下,你会忘记处理不返回值的情况。当然不是故意的,但可能你没有考虑到系统的所有可能状态;或者你可能真的覆盖了所有情况,但随着时间的推移,代码被重构,其中一些检查可能变得无效或不完整。突然之间,你可能会在不知不觉中忘记处理没有值的情况。结果,你有时会收到致命的 PHP 错误,告诉你在一个非对象上调用了一个方法;用户可能会看到空白页面,更糟糕的是。
一方面,Option
强迫开发者有意识地思考两种情况(返回值或不返回值)。这本身就会让你的代码更健壮。另一方面,Option
还允许 API 开发者提供更简洁的 API 方法,并赋予 API 用户消费这些方法的能力。
📦 安装
该项目可以通过 Composer 安装。
$ composer require unicorn-fail/php-option
基本用法
在您的 API 中使用 Option
<?php use UnicornFail\PhpOption\None; use UnicornFail\PhpOption\Some; class MyRepository { public function findSomeEntity($criteria) { if (null !== $entity = $this->entityManager->find($criteria)) { return Some::create($entity); } // Use a singleton for the None case (it's statically cached for performance). return None::create(); } }
如果您正在使用现有的库,您还可以使用一个简短的版本,该版本默认将 null
视为 None
,将其他所有内容视为 Some
情况。
之后
<?php use UnicornFail\PhpOption\Option; class MyRepository { public function findSomeEntity($criteria) { return Option::create($this->entityManager->find($criteria)); // Or, if you want to change the none value to false for example: return Option::create($this->em->find($criteria), ['noneValue' => false]); } }
情况 1:在调用代码时始终需要实体
$entity = $repo->findSomeEntity($criteria)->get(); // Returns an Entity, or throws exception.
情况 2:如果不可用,则回退到默认值
$entity = $repo->findSomeEntity($criteria)->getOrElse(new Entity); // Or, if you need to lazily create the entity. $entity = $repo->findSomeEntity($criteria)->getOrCall(function() { return new Entity; });
不再需要样板代码
之前
$entity = $this->findSomeEntity(); if ($entity === null) { throw new NotFoundException(); } return $entity->name;
之后
return $this->findSomeEntity()->get()->name;
不再有控制流异常
之前
try { $entity = $this->findSomeEntity(); } catch (NotFoundException $ex) { $entity = new Entity; }
之后
$entity = $this->findSomeEntity()->getOrElse(new Entity);
简洁的 null 处理
之前
$entity = $this->findSomeEntity(); if ($entity === null) { return new Entity; } return $entity;
之后
return $this->findSomeEntity()->getOrElse(new Entity);
链式多个替代选项
如果您需要尝试多个替代方案,orElse
方法允许您优雅地完成。
之前
$entity = $this->findSomeEntity(); if ($entity === null) { $entity = $this->findSomeOtherEntity(); if ($entity === null) { $entity = $this->createEntity(); } } return $entity;
之后
return $this->findSomeEntity() ->orElse($this->findSomeOtherEntity()) ->orElse($this->createEntity());
返回第一个非空选项。这对于惰性评估的选项特别有用。
惰性评估的选项
上述示例中存在一个缺陷,即选项链需要在方法调用时评估所有选项。如果第一个选项已经非空,这会产生不必要的开销。
幸运的是,这可以通过使用 LazyOption
来解决,它仅在实际需要时调用一个可调用对象。
use UnicornFail\PhpOption\LazyOption; return $this->findSomeEntity() ->orElse(LazyOption::create([$this, 'findSomeOtherEntity'])) ->orElse(LazyOption::create([$this, 'createEntity']));
类型化选项
在您需要返回特定 PHP 类型(例如字符串、布尔值、数字等)的情况下,TypedOption
类可能为您提供更多灵活性。
之前
// ?coords=32:43,35:22,94:33,95:34 $coordsStr = !!(isset($_GET['coords']) ? $_GET['coords'] : ''); $coords = $coordsStr ? array_map('trim', explode(',', $coordsStr)) : []; foreach ($coords as $coord) { list ($x, $y) = array_map('trim', explode(':', $coord)); $this->doSomething($x, $y); }
之后
use UnicornFail\PhpOption\TypedOption; // Automatically parsed by the SomeArray typed option. // ?coords=32:43,35:22,94:33,95:34 $items = TypedOption::pick($_GET, 'coords', ['keyDelimiter' => ':'])->getOrElse([]); foreach ($items as $x => $y) { $this->doSomething($x, $y); }
📓 API 文档
官方和详细的 API 文档即将推出 (PRs 受欢迎)。
🏷️ 版本控制
严格遵循 SemVer。次要和补丁版本不应引入代码库的破坏性更改。
该项目最初发布将从版本 1.6.0
开始,以与现有的 [phpoption/phpoption](https://packagist.org.cn/packages/phpoption/phpoption) 发布保持一致。
🛠️ 支持 & 向后兼容性
版本 <1.6.0
- 本项目将不会修复任何错误、解决任何安全问题或进行其他发布。请升级,这应该像以下这样简单
$ composer remove phpoption/phpoption $ composer require unicorn-fail/php-option
版本 >=1.6.0 <2.0.0
- 本项目将保持与 phpoption/phpoption 的向后兼容性,并继续运行 原始测试,以确保之前的命名空间和实现仍然有效。
- 以下类将自动注册为别名,因此现有代码应该仍然有效(请参阅下面的已知问题)
PhpOption\LazyOption
<=>UnicornFail\PhpOption\LazyOption
PhpOption\None
<=>UnicornFail\PhpOption\None
PhpOption\Option
<=>UnicornFail\PhpOption\Option
PhpOption\Some
<=>UnicornFail\PhpOption\Some
- 以下方法已弃用,请使用它们的替代品
Option::ensure()
<=>Option::create()
Option::fromValue()
<=>Option::create()
Option::fromArraysValue()
<=>Option::pick()
Option::fromReturn()
<=>Option::create()
$option->ifDefined()
<=>$option->forAll()
- 已知问题
版本 2.0.0
- 本项目计划移除对所有
PHP 5
、PHP 7.0
、PHP 7.1
的支持,以及与 phpoption/phpoption 的向后兼容性。
⛔ 安全性
要报告安全漏洞,请使用 Tidelift 安全联系人。Tidelift 将与我们一起协调修复和披露。
👷♀️ 贡献
对本库的贡献是 欢迎的!
有关更多信息,请参阅 CONTRIBUTING。
🧪 测试
本地开发(忽略对 composer.json
的更改)
$ composer require-test
$ composer test
包含覆盖率
$ composer require-test $ composer require-coverage $ composer test-coverage
🚀 性能基准
当然,性能很重要。测试中包含了一个性能基准,您可以在您选择的机器上运行
$ composer test-group performance
在核心上,使用 Option
产生的开销主要是创建一个对象,即 Option
包装器所花费的时间。它还需要执行一个额外的方法调用以从包装器检索值。根据您的用例和所需功能,您可能会遇到不同的结果。
每次调用的平均开销*
如表上所示,这些基准很少低于微秒的几分之一。其中许多测量在纳秒级别;随着较新版本的 PHP 逐渐降低开销。
除非您计划在请求期间调用方法数十万次,否则没有必要坚持使用 object|null
返回值;最好给您的代码一些选择!
*基于创建单个对象与创建包装器和单个方法调用的比较;迭代 10000 次,然后计算差异的平均值。
👥 致谢
📄 许可证
unicorn-fail/php-option 采用 Apache 2.0 许可证。更多详情请参阅 LICENSE
文件。