spindle / types
PHP对象系统的基本类型
Requires
- php: >=5.3.2
Requires (Dev)
- ext-pdo_sqlite: *
- pdepend/pdepend: *
- phpunit/phpunit: *
This package is not auto-updated.
Last update: 2024-09-14 15:32:20 UTC
README
PHP通过提供基础类群来实现强类型。
$ composer require 'spindle/types:*'
基本类型
Spindle\Types\Enum
继承Enum后变为枚举类型。实例可以保证是类中定义的const的某个值。
<?php final class Suit extends Spindle\Types\Enum { const SPADE = 'spade' , CLUB = 'club' , HEART = 'heart' , DIAMOND = 'diamond' } $spade = new Suit(Suit::SPADE); $spade = Suit::SPADE(); //syntax sugar echo $spade, PHP_EOL; echo $spade->valueOf(), PHP_EOL; function doSomething(Suit $suit) { //$suitは必ず4種類のうちのどれかである }
Spindle\Types\TypedObject
继承TypedObject后可以创建具有固定属性类型的类。可以更安全地处理复杂数据。可用于实现领域驱动设计中的"实体"或"值对象"。
类型通过静态方法schema()定义。schema需要返回一个数组,该数组是重复的,格式为属性名 => 类型, 默认值
。默认值可以省略,此时将设置为null。
<?php class User extends Spindle\Types\TypedObject { static function schema() { return array( 'firstName' => self::STR, 'lastName' => self::STR, 'age' => self::INT, 'birthday' => 'DateTime', new DateTime('1990-01-01'), ); } function checkErrors() { $errors = array(); if ($this->age < 0) { $errors['age'] = 'ageは0以上である必要があります'; } return $errors; } } $taro = new User; $taro->firstName = 'Taro'; $taro->lastName = 'Tanaka'; $taro->age = 20; //$taro->age = '20'; とすると、InvalidArgumentExceptionが発生して停止する
以下是可以指定的类型值。
- self::BOOL (布尔值)
- self::INT (整数)
- self::DBL (浮点数)
- self::STR (字符串)
- self::ARR (数组)
- self::OBJ (对象)
- self::RES (资源)
- self::CALL (回调函数)
- self::MIX (未指定类型)
- className 类名/接口名。使用完全限定名指定。对于类名,使用instanceof进行判定。
由于固定了__get()
和__set()
为final,继承TypedObject将部分剥夺类的能力。不建议所有类都从TypedObject派生。
TypedObject支持foreach (IteratorAggregate)。TypedObject可以通过count()函数计算元素数量。(Countable)
推荐实现checkErrors()
方法。这是一个执行无法通过类型检查的验证的方法。默认实现检查所有属性是否为not null。
TypedObject::$preventExtensions
TypedObject默认情况下拒绝schema中未定义的属性的赋值和引用。这有助于更容易地发现属性错误,但也可能不方便。
将TypedObject::$preventExtensions设置为false时,不拒绝未定义的属性,而是自动扩展。(默认为true)
此外,扩展的属性将自动被处理为mixed(不进行类型检查)。
<?php use Spindle\Types; class MyObj extends Types\TypedObject { static function schema() { return array( 'a' => self::INT, 'b' => self::BOOL, ); } } $obj = new MyObj; Types\TypedObject::$preventExtensions = false; $obj->c = 'str'; //エラーは起きない Types\TypedObject::$preventExtensions = true; $obj->c = 'str'; //例外発生
TypedObject::$casting
TypedObject在属性赋值时,如果schema和类型不匹配,则抛出异常。但是,有时也想要像PHP标准行为一样,在尝试赋值不同类型时执行类型转换。例如,从数据库中获取字符串并恢复对象。
将TypedObject::$casting设置为true时,即使尝试赋值不同类型,也会尽可能进行转换。
PDO::FETCH_CLASS的配合
可以将从数据库SELECT获取的结果流到TypedObject中。PDO有一个标准功能,可以直接使用PDO::FETCH_CLASS
模式来创建类实例,因此建议使用它。通常,从PDO返回的数据是string类型,因此请确保启用$casting
选项。
<?php use Spindle\Types; class User extends Types\TypedObject { static $casting = true; static function schema() { return array( 'userId' => self::INT, 'name' => self::STR, 'age' => self::INT ); } } $pdo = new PDO('sqlite::memory:', null, null, array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, )); $pdo->exec('CREATE TABLE User(userId INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)'); $pdo->exec('INSERT INTO User(name, age) VALUES("taro", 20)'); $pdo->exec('INSERT INTO User(name, age) VALUES("hanako", 21)'); $stmt = $pdo->prepare('SELECT * FROM User'); $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, __NAMESPACE__ . '\\RowModel'); $stmt->execute(); foreach ($stmt as $row) { self::assertInternalType('integer', $row->userId); self::assertInternalType('string', $row->name); self::assertInternalType('integer', $row->age); }
需要注意的是,PDO::FETCH_CLASS
的行为与常规对象初始化和操作不同,它首先执行setter,然后启动构造函数。由于TypedObject在构造函数中初始化对象,因此这种操作可能不会正常工作。
使用PDO::FETCH_CLASS
时,必须同时指定PDO::FETCH_PROPS_LATE
。此选项指定后,构造函数将先启动,从而正常工作。
TypedObject的继承
继承由TypedObject创建的类时,父类的schema不会自动继承。需要使用extend方法显式扩展。
<?php class Employee extends Spindle\Types\TypedObject { static function schema() { return array( 'id' => self::INT, 0, 'name' => self::STR, ); } } class Boss extends Employee { static function schema() { return self::extend(parent::schema(), array( 'room' => self::INT, )); } }
Spindle\Types\ConstObject
ConstObject是使TypedObject变为不可变对象的装饰器。
<?php $const = new ConstObject($typedObject); echo $const->foo; //参照は透過的に可能 //$const->foo = 'moo'; どのプロパティに対しても代入操作は常に例外を発生させる
Spindle\Types\Collection
对array()添加了一些限制的数组。
- 仅允许数值索引
- 顺序得到保证
- 可以按需固定元素的类型
Spindle\Types\ConstCollection
将Collection变为只读的装饰器。
Polyfill
PHP从5.4和5.5开始可以使用一些标准接口。为了在PHP5.3中使用它们,提供了Polyfill。
为了确保如DateTime implements DateTimeInterface
等状态,它们被放置在自定义命名空间上。
Spindle\Types\Polyfill\JsonSerializable
相当于PHP5.4以后的JsonSerializable
接口。
Spindle\Types\Polyfill\DateTimeInterface
相当于PHP5.5以后的DateTimeInterface
接口。
Spindle\Types\Polyfill\DateTime
实现了DateTimeInterface
的DateTime。
Spindle\Types\Polyfill\DateTimeImmutable
相当于PHP5.5以后的DateTimeImmutable
。不能修改状态,在modify或setTimestamp等方法被调用时,将返回一个新的实例。
许可
spindle/types的版权已放弃。使用时没有限制,不需要联系作者或显示版权声明。即使是代码片段也可以自由复制使用。
CC0-1.0 (无权利保留)