serafim/dbc 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

serafim/dbc

Composer 安装命令:

composer require serafim/dbc

包简介

Design by Contract framework for PHP

README 文档

README

Contracts for PHP, is a contract programming framework and test tool for PHP, which uses attributes to provide run-time checking. (In particular, this is not a static analysis tool.)

Latest Stable Version Latest Unstable Version Total Downloads License MIT

Contents

Requirements

  • PHP 8.1+

Installation

Library is available as composer repository and can be installed using the following command in a root of your project.

$ composer require serafim/dbc

Configuration

By default, the behavior of contracts depends on whether assertions (assert.active in php.ini configuration) are enabled on your system.

However, you can force them to enable or disable contracts:

use Serafim\Contracts\Runtime;

// Enable runtime contract assertions
Runtime::enable();

// Disable runtime contract assertions
Runtime::disable();

// Enable runtime contract assertions if PHP
// assertions are enabled or disable otherwise.
Runtime::auto();

By default, the framework does not listen to any namespaces. To add a namespace for your application, use the Runtime::listen() method.

use Serafim\Contracts\Runtime;

Runtime::listen('App\\Entity', 'App\\Http\\Controllers');

In addition, you can specify the directory where the cache files should be stored.

Please note that these cache files are included by the PHP, therefore they are cached by opcache extension and do not degrade performance.

use Serafim\Contracts\Runtime;

Runtime::cache(__DIR__ . '/storage');

Usage

Contracts are written as PHP code within quoted strings, embedded in attributes. E.g., #[Verify('$x < 100')] states that $x must be less than 100. Any PHP expression, except anonymous classes, may be used, provided the string is properly escaped.

An annotation binds a contract to a code element: either a method or a class. Library defines three main annotation types, which live in the Serafim\Contracts\Attribute namespace:

  • #[Verify] for method preconditions;
  • #[Ensure] for method postconditions;
  • #[Invariant] for class invariants;

Contract annotations work only (yet) with classes.

Invariants

A class may have associated invariants. Instead of specifying a contract between a caller and a callee, those invariants describe the state of a valid object of the qualified type. Calling methods on an object may cause it to change; invariants guarantee that after any such change, the object remains in a consistent state.

Of course, internal operations are allowed to muck around and temporarily invalidate invariants to do their job, but they agree to eventually put everything back into their proper places. Intuitively, any operation made against this is considered internal and does not need to obey the invariants. Only method invocations on other variables do.

Any defined invariant in a class has access to all of its fields, including any protected and private.

use Serafim\Contracts\Attribute\Invariant;

#[Invariant('$this->balance >= 0')]
class Account
{
    private int $balance = 0;
    
    public function deposit(int $amount): void
    {
        $this->balance += $amount + 1;
    }
}

When determining such an invariant, the account's balance will always be greater or equal than zero.

$account = new Account();

$account->deposit(42);      // OK
$account->deposit(-666);    // Serafim\Contracts\Exception\InvariantException: $this->balance >= 0

Method contracts

A method may have preconditions and postconditions attached to it. Together, they specify the contract between caller and callee: if the precondition is satisfied on entry of the method, then the caller may assume the postcondition on exit. The precondition is what the callee demands of the caller, and in return the caller expects the postcondition to hold after the call.

As an example, consider the following specification of the square root function, which states that for any non-negative double x given, sqrt will return a non-negative result.

use Serafim\Contracts\Attribute\Verify;
use Serafim\Contracts\Attribute\Ensure;

class Math
{
    #[Verify('x >= 0')]
    #[Ensure('$result >= 0')]
    public static function sqrt(float $x): float 
    {
        // ...code
    }
}

As shown in this example, a precondition may access parameter values; in fact, preconditions and postconditions are evaluated in the context of the method they are bound to. More precisely, each annotation behaves as if it were a method, with the same arguments and in the same scope as the qualified method. In terms of scoping, the previous code is equivalent to the following:

use Serafim\Contracts\Attribute\Verify;
use Serafim\Contracts\Attribute\Ensure;

class Math
{
    public static function sqrt(float $x): float 
    {
        if ($x < 0) {
            throw new \Serafim\Contracts\Exception\PreconditionException('$x >= 0');
        }
        
        // ...code
        
        if ($result < 0) {
            throw new \Serafim\Contracts\Exception\PostconditionException('$result >= 0');
        }
        
        return $result;
    }
}

In addition, postconditions may contain a few extensions:

  • As we have seen, they may refer to the returned value, using the $result keyword.
  • Within a postcondition,$old is a keyword that contains the state of the object before its changes.

At run time, when contracts are enabled, preconditions and postconditions translate to checks on entry and exit, respectively, of the method. A failure results in a PreconditionException or PostconditionException being thrown, depending on the origin: failure to meet a precondition means that the method was called incorrectly, whereas an unsatisfied postcondition points to a bug in the implementation of the method itself.

Keywords

Keyword May appear in Description
$old #[Ensure] Value on method entry
$result #[Ensure] Value to be returned

Features

  • Class Contracts
    • Abstract methods
    • Inheritance (import from parent classes, traits and interfaces)
  • Trait Contracts
    • Abstract methods
  • Interface Contracts

serafim/dbc 适用场景与选型建议

serafim/dbc 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 1.28k 次下载、GitHub Stars 达 3, 最近一次更新时间为 2022 年 03 月 25 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 serafim/dbc 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 serafim/dbc 我们能提供哪些服务?
定制开发 / 二次开发

基于 serafim/dbc 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 1.28k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 3
  • 点击次数: 6
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 3
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2022-03-25