1.1.1 2024-09-09 10:08 UTC

This package is auto-updated.

Last update: 2024-09-09 10:14:09 UTC


README

laze 是一个用于定义 惰性求值 的 PHP 库。值被设置为闭包,只有在首次访问时才会实现,确保高效且可控的初始化。一旦闭包函数被评估,它就变为不可变的,不能重新定义为不同的值。然而,它可以在访问之前重新定义为闭包,在访问时转换为非闭包值。

laze 可能是一个表示放松或懒惰的英文单词,但在这种情况下,它实际上是一个由 Lazy Evaluation 衍生的缩写。这指的是一种编程技术,其中表达式的评估被延迟到其值需要时。使用 laze,一旦值被评估,它就变成一个不可变值。换句话说,一个虽然延迟评估,但在初始评估后不能修改的值。因此,laze 封装了导致最终值的延迟评估概念,将灵活性和鲁棒性结合到一个概念中。

安装

您可以使用 Composer 安装 laze

composer require divengine/laze

用法

定义一个惰性常量

要定义一个惰性常量,使用 laze::define 方法。值必须提供为闭包。

use divengine\laze;

// Define a lazy constant
laze::define('MY_CONSTANT', fn() => computeExpensiveValue());

读取惰性常量

要访问值,使用 Laze::read 方法。闭包将在第一次访问时被评估,并将结果存储为常量的值。

$value = laze::read('MY_CONSTANT');

基本示例

use divengine\laze;

// Define a lazy constant. Simulate an expensive computation
laze::define('MY_CONSTANT', fn() => return rand(1, 100)); 

// First access, the closure is evaluated
$value = laze::read('MY_CONSTANT');
echo $value; // Outputs the evaluated value

// Subsequent access returns the stored value
$value = laze::read('MY_CONSTANT');
echo $value; // Outputs the same value as before

综合示例

此示例以简洁的方式概述了 Laze 的全部功能,适合 README 文件,展示了它在实际场景中的应用。

  • 约束:确保 APP_CONFIG 实现 Configurable 接口。
  • 返回闭包的闭包:MY_FUNCTION 键持有一个返回另一个闭包的闭包
  • 具有对象实例的惰性值:APP_CONFIG 存储了一个 AppConfig 实例,该实例通过约束进行验证。
  • 重复定义和读取:FINAL_MESSAGE 从 APP_CONFIG 和 MY_FUNCTION 读取,结合它们的值。
  • PHPUnit 测试:演示了如何使用模拟对象重新定义 APP_CONFIG 进行测试。
interface Configurable {
    public function configure(array $settings): void;
}

class AppConfig implements Configurable {
    private array $settings;

    public function configure(array $settings): void {
        $this->settings = $settings;
    }

    public function getSetting(string $key) {
        return $this->settings[$key] ?? null;
    }
}

// 1. Add a constraint to ensure a value implements the Configurable interface
laze::constraint(
    name: 'Must implement Configurable interface',
    fn($key, $value) => $key == 'APP_CONFIG' ? $value instanceof Configurable : true
);

// 2. Define a lazy value that returns a closure
laze::define('MY_FUNCTION', function() {
    return function() {
        return "Function Result";
    };
});

// 3. Define a lazy value with an object instance
laze::define('APP_CONFIG', function() {
    $config = new AppConfig();
    $config->configure([
        'timezone' => 'UTC',
        'locale' => 'en_US'
    ]);
    return $config;
});

// 4. Reuse define and read within each other
laze::define('FINAL_MESSAGE', function() {
    $config = laze::read('APP_CONFIG');
    $timezone = $config->getSetting('timezone');
    return laze::read('MY_FUNCTION')() . " in timezone $timezone";
});

$finalMessage = laze::read('FINAL_MESSAGE');
echo $finalMessage; // Outputs: "Function Result in timezone UTC"

// 5. PHPUnit Test - Redefining a value
class LazeTest extends \PHPUnit\Framework\TestCase {
    public function testAppConfigCanBeMocked() {

        // mock function
        laze::define('MY_FUNCTION', function() {
            return function() {
                return "Mocked Result";
            };
        });

        // mock object
        laze::define('APP_CONFIG', function() {
            $mockConfig = $this->createMock(Configurable::class);
            $mockConfig->method('getSetting')->willReturn('mocked_timezone');
            return $mockConfig;
        });

        $message = laze::read('FINAL_MESSAGE');
        $this->assertEquals("Mocked Result in timezone mocked_timezone", $message);
    }
}

如何在测试期间重置 Laze

如果您需要在测试期间重置 Laze,您有两个选择

  1. 使用反射重置 Laze 的内部状态。

    class LazeTest extends \PHPUnit\Framework\TestCase {
    
        public $lazeBackup;
    
        public function setUp(): void {
            parent::setUp();
            $reflection = new ReflectionClass(laze::class);
            $property = $reflection->getProperty('store');
            $property->setAccessible(true);
            $this->lazeBackup = $property->getValue();
        }
    
        public function tearDown(): void {
            $reflection = new ReflectionClass(laze::class);
            $property = $reflection->getProperty('store');
            $property->setAccessible(true);
            $property->setValue($this->lazeBackup);
            parent::tearDown();
        }
    }
  2. 如果您使用 phpunit,则使用带有参数 --process-isolation 的它来在单独的进程中运行每个测试。

    vendor/bin/phpunit --process-isolation

此库的用途

  • 惰性求值:通过延迟值评估直到需要时,优化资源使用,提高性能和加载时间。

  • 不可变性:确保值在评估后保持不变,在并发环境和函数式编程中非常有用。

  • 依赖注入:支持依赖的惰性初始化,提高模块化和测试(例如,模拟服务)。

  • 配置管理:管理特定于环境或条件的配置,仅在需要时评估。

  • 缓存:实现延迟缓存,仅在需要时存储结果,并支持具有约束的多级缓存。

  • 事件驱动编程:便于延迟事件处理,仅在特定条件或请求下触发操作。

  • 测试:在单元测试中使用约束验证值,并使用惰性加载的依赖模拟复杂环境。

  • 安全:在使用值之前强制执行数据验证和安全约束,降低风险。

  • 声明式编程:支持按需评估的声明式配置。

  • 领域特定语言(DSLs):使用声明式值定义构建DSLs,在特定上下文中执行。

  • CI/CD:定义CI/CD管道的动态配置或脚本,根据环境状态条件评估。

许可证

本项目受GNU通用公共许可证(GPL)许可。请参阅LICENSE文件以获取详细信息。

贡献

欢迎贡献!请随时提交拉取请求或开启一个问题。

关于

lazeDivengine软件解决方案开发和维护。如果您觉得这个库很有用,请考虑给它点星并与其他人分享。