osmphp/framework

Osm Framework 是一个开源的、极快、前所未有可扩展、并且令人愉快的 PHP 8 框架,用于创建现代 Web 应用程序。它建立在经过考验和测试的 Symfony 和 Laravel 组件之上。

v0.15.17 2022-05-30 08:40 UTC

README

Build Status Total Downloads Latest Stable Version License

Osm Framework 是一个开源的、极快、前所未有可扩展、并且令人愉快的 PHP 8 框架,用于创建现代 Web 应用程序。它建立在经过考验和测试的 Symfony 和 Laravel 组件之上。

文档

  • 入门
  • 编写 PHP 代码
  • 创建 Web 应用程序
    • 请求
    • 路由
    • 区域
    • 建议
    • 响应
    • 视图和组件
    • 主题
    • 资源
  • 创建控制台应用程序
  • 处理数据
  • 编写 JavaScript 代码
    • 控制器
    • 测试
  • 其他功能
    • 日志记录
    • 缓存
    • 配置
    • 翻译
    • 维护模式
    • 生产模式
    • 辅助函数
    • 扩展 Gulp 脚本
  • 许可证

主要功能

可扩展性(动态特性)

让我们更详细地看看可扩展性。

想象一下使用某些电子商务软件,按照以下方式处理新的销售订单

class Order extends Object_ {
    public function submit(): void {
        $this->validate();
        $this->applyDiscounts();
        $this->applyTaxes();
        $this->save();
    }
    
    protected function validate(): void {
        // standard validation logic
        ...
    }
    
    ...
}

如果您需要自定义此逻辑,例如,检查所购买的商品是否都有库存,您可以在验证阶段之后添加它

trait OrderTrait {
    protected function around_validate(callable $proceed): void {
        // first, execute the standard validation
        $proceed();
        
        // then, add the logic that checks the stock
        ...
    }
} 

在底层,Osm 框架将此 PHP 特性动态应用于 Order 类,并用您的自定义 around_validate() 方法覆盖标准 validate() 方法。

这样,您可以自定义几乎所有类中的任何方法。

您还可以向现有类中引入新的属性和方法

/**
 * @property bool @are_all_items_in_stock   
 */
trait OrderTrait {
    protected function checkStockItem(): bool {
        ...
    } 
} 

快速且易于测试的计算属性

在 Osm 框架中,计算(或“延迟”)属性 是 PHP 类的公共属性,它首次访问时通过匹配的 get_ 方法进行一次计算。

例如,考虑一个读取并将 Markdown 文件转换为 HTML 的类

/**
 * @property string $path Relative file path in the `data` directory. 
 *      Provide this property in the constructor.
 * @property string $absolute_path Absolute file path
 *
 * @property string $text Original text in Markdown format
 * @property string $html Text converted to HTML
 */
class MarkdownFile extends Object_ {
    protected function get_absolute_path(): string {
        // get the reference to the global application object which,
        // among other things, stores the absolute path of the `data`
        // directory in its `paths->data` property 
        global $osm_app; /* @var App $osm_app */

        return "{$osm_app->paths->data}/posts/{$this->path}";
    }

    protected function get_text(): string {
        return file_get_contents($this->absolute_path);
    }

    protected function get_html(): ?string {
        // convert the text into HTML using `michelf/php-markdown` 
        // Composer package
        return MarkdownExtra::defaultTransform($this->text);
    }
} 

典型用法

// `MarkdownFile::new()` creates new instance of the class, 
// just as `new MarkdownFile()` would do, plus it applies dynamic traits
$file = MarkdownFile::new(['path' => 'welcome.md']);

echo $file->html;

在首次访问形式上未定义的 html 属性时,PHP 内部创建它,并使用 get_html() 方法计算其值。在后续访问时,PHP 仅返回之前计算过的属性值。相同的情况也发生在 textabsolute_path 属性上。

计算属性节省宝贵的 CPU 周期。一方面,只有当属性实际被访问时才会计算属性值。另一方面,某些属性在处理单个 HTTP 请求时被访问数百或数千次,而由于非常快速的后续访问,这些属性具有显著的性能提升。

计算属性也易于测试。在以下示例中,单元测试完全专注于 HTML 转换,通过在构造函数中提供其值来省略 text 属性的计算——以及所有文件处理。

public function test_markdown_transformation() {
    // GIVEN a bold text written in Markdown
    $file = MarkdownFile::new(['text' => '**test**']);
    
    // WHEN you convert it to HTML
    // THEN it is marked with `<strong>` HTML element
    $this->assertEquals('<p><strong>test</strong></p>', $file->html); 
}     

使用反射以更少的努力获得更多结果

使用 Osm 框架,您可以通过让其从类定义中推断平凡的事情来更快地开发。

例如,为了引入新的控制台命令,您只需定义一个扩展 Command 类的类,框架会自动将其添加到系统中。它还会检查属性定义,找到 OptionArgument 属性,并将它们公开为命令行选项和参数。

/**
 * @property bool $caps #[Option] If specified, the person name is upper-cased
 * @property string $person_name #[Argument] The person to greet
 */
class Hello extends Command
{
    public string $name = 'hello';
    public string $description = 'A sample command';

    public function run(): void {
        $name = $this->caps ? strtoupper($this->person_name) : $this->person_name;
        $this->output->writeln("Hello, {$name}");
    }
}

在运行 gulp watch 时,您可以直接使用此命令

>osm hello vo
Hello, vo

>osm hello vo --caps
Hello, VO 

另一个示例通过将属性标记为 Cached 来将其存储在应用程序缓存中

/**
 * @property string $cached_property #[Cached('my_cache_entry')]
 */
class MyClass extends Object_
{
    protected function get_cached_property(): string {
        ...
    }
}       

卓越性能

Osm框架非常快,原因有两个。

首先,它将性能密集的部分卸载到预执行(或“编译”)阶段,并积极使用缓存技术。

其次,在真正需要的地方,它将性能放在首位,有时甚至超过既定的编程实践。

一个例子是计算属性的实现。实现非常快,但它牺牲了封装原则——计算属性是公开的。

另一个例子是全局变量$osm_app。一般来说,全局变量是一个已知的反模式。然而,根据测试显示,用直接变量访问替换get_app()访问器函数可以显著提高性能,因此,$osm_app全局变量成为了主要内部API入口点。