lukaszmakuch/haringo

序列化对象的创建方式,而不是对象本身。

v0.0.4 2016-02-06 19:51 UTC

This package is not auto-updated.

Last update: 2024-09-20 17:27:27 UTC


README

序列化创建对象的方式,而不是对象本身!

travis

什么是Haringo?

Haringo是一个库,旨在解决经典序列化中的主要问题

  • 丢失引用,导致存在使用全局变量的__wakeup()方法
  • 在移动或重命名序列化对象的类后,数据损坏

这是如何实现的?

不再需要__wakeup()!

Haringo允许描述对象应该如何构建,然后序列化该描述而不是创建的对象。正是因为这一点,任何基于序列化构建计划构建的对象都不需要了解有关全局资源注册表或当前用户会话等信息。

重命名自由!

使用Haringo,您可以使您的序列化数据完全摆脱以下内容

  • 类路径
  • 方法名称
  • 参数名称

这是通过可配置的基于映射的源和选择器实现的。

目录

  1. 获取Haringo

安装

使用 composer 获取最新版本

$ composer require lukaszmakuch/haringo

构建Haringo

要构建Haringo,您可以使用 \lukaszmakuch\Haringo\Builder\HaringoBuilder 接口的自定义实现。

获取构建器

<?php
use lukaszmakuch\Haringo\Builder\Impl\HaringoBuilderImpl;

$haringoBuilder = new HaringoBuilderImpl();

获取基本Haringo实例

如果您不打算以某种方式扩展您的Haringo实例,您可以直接调用 HaringoBuilder::build():Haringo 方法。

<?php
use lukaszmakuch\Haringo\Builder\HaringoBuilder;

/* @var $haringoBuilder HaringoBuilder */
$haringo = $haringoBuilder->build();

设置映射

为了使用 ClassPathFromMapMethodFromMapParamFromMap,您需要使用您能够配置的映射实例构建Haringo。

ClassPathSourceMap

创建映射

要创建类路径源映射,您只需要创建一个新的 ClassPathSourceMap 实例。

<?php
use lukaszmakuch\Haringo\ClassSourceResolver\Impl\ClassPathFromMapResolver\ClassPathSourceMap;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;

$map = new ClassPathSourceMap();
将映射添加到Haringo

新的映射必须在将其用于构建新的Haringo之前传递给Haringo构建器。但是,它可以在稍后进行配置,因为它作为引用传递。

<?php
use lukaszmakuch\Haringo\Builder\HaringoBuilder;
use lukaszmakuch\Haringo\ClassSourceResolver\Impl\ClassPathFromMapResolver\ClassPathSourceMap;

/* @var $haringoBuilder HaringoBuilder */
/* @var $map ClassPathSourceMap */
$haringoBuilder->setClassSourceMap($map);

MethodSelectorMap

要创建类路径源映射,您只需要创建一个新的 MethodSelectorMap 实例。

创建映射
<?php
use lukaszmakuch\Haringo\MethodSelectorMatcher\Impl\MethodFromMap\MethodSelectorMap;

$map = new MethodSelectorMap();
将映射添加到Haringo

新的映射必须在将其用于构建新的Haringo之前传递给Haringo构建器。但是,它可以在稍后进行配置,因为它作为引用传递。

<?php
use lukaszmakuch\Haringo\Builder\HaringoBuilder;
use lukaszmakuch\Haringo\MethodSelectorMatcher\Impl\MethodFromMap\MethodSelectorMap;

/* @var $haringoBuilder HaringoBuilder */
/* @var $map MethodSelectorMap */
$haringoBuilder->setMethodSelectorMap($map);

ParamSelectorMap

要创建类路径源映射,您只需要创建一个新的 ParamSelectorMap 实例。

创建映射
<?php
use lukaszmakuch\Haringo\ParamSelectorMatcher\Impl\ParamFromMapMatcher\ParamSelectorMap;

$map = new ParamSelectorMap();
将映射添加到Haringo

新的映射必须在将其用于构建新的Haringo之前传递给Haringo构建器。但是,它可以在稍后进行配置,因为它作为引用传递。

<?php
use lukaszmakuch\Haringo\Builder\HaringoBuilder;
use lukaszmakuch\Haringo\ParamSelectorMatcher\Impl\ParamFromMapMatcher\ParamSelectorMap;

/* @var $haringoBuilder HaringoBuilder */
/* @var $map ParamSelectorMap */
$haringoBuilder->setParamSelectorMap($map);

扩展

ValueSourceExtension

可以添加对不同的 ValueSource 实现的支持。每个扩展都必须实现 \lukaszmakuch\Haringo\Builder\Extension\ValueSourceExtension 接口,并在构建新的Haringo之前将其传递给Haringo构建器。有关创建自己的Haringo扩展的说明,请参阅相关描述。

<?php
use lukaszmakuch\Haringo\Builder\HaringoBuilder;
use lukaszmakuch\Haringo\Builder\Extension\ValueSourceExtension;

/* @var $haringoBuilder HaringoBuilder */
/* @var $extension ValueSourceExtension */
$haringoBuilder->addValueSourceExtension($extension);

使用Haringo

构建计划

每个构建计划都描述了如何获取某个类的实例。为了文档(和测试)目的,让我们使用这个简单的类

<?php
namespace lukaszmakuch\Haringo;

class TestClass
{
    public $memberA;
    public $memberB;
    public $passedToConstructor;

    public function __construct($passedToConstructor = null)
    {
        $this->passedToConstructor = $passedToConstructor;
    }

    public function setMembers($newA = null, $newB = null)
    {
        $this->memberA = $newA;
        $this->memberB = $newB;
    }
}

新建实例构建计划

描述如何构建某个类的新实例。它需要一个类的来源以进行实例化,以及可选的方法调用,这些方法将在新实例上调用。

等于以下构建计划的结果的代码

<?php
use lukaszmakuch\Haringo\TestClass;

$obj = new TestClass(constructorParam);
$obj->setMembers("firstParamVal", "secondParamVal");
示例构建计划
<?php
use lukaszmakuch\Haringo\BuildPlan\Impl\NewInstanceBuildPlan;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;
use lukaszmakuch\Haringo\MethodCall\MethodCall;
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByExactName;
use lukaszmakuch\Haringo\ParamValue\AssignedParamValue;
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;
use lukaszmakuch\Haringo\TestClass;

$plan = (new NewInstanceBuildPlan())
    ->setClassSource(new ExactClassPath(TestClass::class))
    ->addMethodCall(
        (new MethodCall(new MethodByExactName("setMembers")))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("newB"),
                new ScalarValue("secondParamVal")
            ))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("newA"),
                new ScalarValue("firstParamVal")
            ))
    )
    ->addMethodCall(
        (new MethodCall(new MethodByExactName("__construct")))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("passedToConstructor"),
                new ScalarValue("constructorParam")
            ))
    );

静态工厂产品构建计划

描述如何获取静态工厂方法的产品。它需要两个东西

  1. 工厂类的来源
  • 静态工厂方法的调用,该方法返回产品

让我们创建一个提供静态工厂方法的类

<?php
use lukaszmakuch\Haringo\TestClass;

class TestStaticFactory
{
    public static function getProduct($configValue)
    {
        return new TestClass($configValue);
    }
}

等于以下构建计划的结果的代码

<?php

$obj = TestStaticFactory::getProduct("paramValue");
示例构建计划
<?php
use lukaszmakuch\Haringo\BuildPlan\Impl\StaticFactoryProductBuildPlan;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;
use lukaszmakuch\Haringo\MethodCall\MethodCall;
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByExactName;
use lukaszmakuch\Haringo\ParamValue\AssignedParamValue;
use lukaszmakuch\Haringo\TestClass;
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;

$plan = (new StaticFactoryProductBuildPlan())
    ->setFactoryClass(new ExactClassPath(TestStaticFactory::class))
    ->setFactoryMethodCall(
        (new MethodCall(new MethodByExactName("getProduct")))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("configValue"),
                new ScalarValue("paramValue")
            ))
    );

工厂对象产品构建计划

描述如何使用工厂对象获取某些产品。它需要两个东西

  1. 值来源,该值解析为工厂对象
  • 返回产品的调用

让我们创建一个提供工厂方法的类

<?php
use lukaszmakuch\Haringo\TestClass;

class TestFactoryClass
{
    public function getProduct($configValue)
    {
        return new TestClass($configValue);
    }
}

等于以下构建计划的结果的代码

<?php

$factory = new TestFactoryClass();
$obj = $factory->getProduct("paramValue");
示例构建计划
<?php
use lukaszmakuch\Haringo\BuildPlan\Impl\FactoryObjectProductBuildPlan;
use lukaszmakuch\Haringo\BuildPlan\Impl\NewInstanceBuildPlan;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;
use lukaszmakuch\Haringo\MethodCall\MethodCall;
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByExactName;
use lukaszmakuch\Haringo\ParamValue\AssignedParamValue;
use lukaszmakuch\Haringo\TestClass;
use lukaszmakuch\Haringo\ValueSource\Impl\BuildPlanResultValue;
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;

$plan = (new FactoryObjectProductBuildPlan())
    ->setFactoryObject(
        //build TestFactoryClass instance
        new BuildPlanResultValue((new NewInstanceBuildPlan())
            ->setClassSource(new ExactClassPath(TestFactoryClass::class)
        ))
    )
    ->setBuildMethodCall(
        (new MethodCall(new MethodByExactName("getProduct")))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("configValue"),
                new ScalarValue("paramValue")
            ))
    );

构建器对象产品构建计划

描述如何使用构建器对象获取某些产品。计划中有两个强制元素

  1. 值来源,该值解析为构建器对象
  • 返回产品的调用。由于它是构建器,也可以调用一些确定进一步结果状态的方法。

让我们创建一个构建器

<?php
use lukaszmakuch\Haringo\TestClass;

class TestBuilder
{
    private $param;
    public function setConstructorParam($param) { $this->param = $param; }
    public function build()
    {
        return new TestClass($this->param);
    }
}

等于以下构建计划的结果的代码

<?php

$builder = new TestBuilder();
$builder->setConstructorParam("paramValue");
$obj = $builder->build();
示例构建计划
<?php
use lukaszmakuch\Haringo\BuildPlan\Impl\BuilderObjectProductBuildPlan;
use lukaszmakuch\Haringo\BuildPlan\Impl\NewInstanceBuildPlan;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;
use lukaszmakuch\Haringo\MethodCall\MethodCall;
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByExactName;
use lukaszmakuch\Haringo\ParamValue\AssignedParamValue;
use lukaszmakuch\Haringo\TestClass;
use lukaszmakuch\Haringo\ValueSource\Impl\BuildPlanResultValue;
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;

$plan = (new BuilderObjectProductBuildPlan())
    ->setBuilderSource(
        //build TestBuilder object
        new BuildPlanResultValue((new NewInstanceBuildPlan())
            ->setClassSource(new ExactClassPath(TestBuilder::class)
        ))
    )
    ->addSettingMethodCall(
        (new MethodCall(new MethodByExactName("setConstructorParam")))
            ->assignParamValue(new AssignedParamValue(
                new ParamByExactName("param"),
                new ScalarValue("paramValue")
            ))
    )
    ->setBuildMethodCall(
        (new MethodCall(new MethodByExactName("build")))
    );

类路径源

精确类路径

表示某个类的完整类路径。

<?php
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;

//source of the \DateTime class
$classSrc = new ExactClassPath(\DateTime::class);

从映射中获取类路径

允许将任何类路径分配给一个字符串键。

在您移动或重命名类时,键保持不变,因此先前序列化的构建计划不会过时。

为了使 Haringo 能够从映射中解析类路径,它必须与映射一起构建

向映射中添加新的路径来源
<?php
use lukaszmakuch\Haringo\ClassSourceResolver\Impl\ClassPathFromMapResolver\ClassPathSourceMap;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;

/* @var $map ClassPathSourceMap */

//add the \DateTime class source under the "date_time" key
$map->addSource(
    //key within the map
    "date_time",
    //actual class path source
    new ExactClassPath(\DateTime::class)
);
使用映射中的类路径
<?php
use lukaszmakuch\Haringo\ClassSource\Impl\ClassPathFromMap;

//class source stored under the "date_time" key
$classSrc = new ClassPathFromMap("date_time");

方法选择器

构造函数选择器

选择构造函数。

<?php
use lukaszmakuch\Haringo\MethodSelector\Impl\ConstructorSelector;

//constructor of any class
$methodSelector = ConstructorSelector::getInstance();

按精确名称选择方法

通过它的精确名称选择某个方法。它也可以是“__constructor”,但为了方便起见,最好使用ConstructorSelector

<?php
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;

//method with name "myMethodName"
$methodSelector = new MethodByExactName("myMethodName");

从映射中选择方法

允许将任何完整的方法标识符分配给一个字符串键。完整的标识符是完整的类路径来源和一些方法选择器。

由于每个完整标识符都包含一个类路径来源,因此可以在同一键下映射两个方法,它们将表示不同类中的不同方法。

在您重命名方法时,键保持不变,因此先前序列化的构建计划不会过时。

为了使 Haringo 能够从映射中获取方法选择器,它必须与映射一起构建

添加新的方法选择器
<?php
use lukaszmakuch\Haringo\MethodSelectorMatcher\Impl\MethodFromMap\MethodSelectorMap;
use lukaszmakuch\Haringo\MethodSelectorMatcher\Impl\MethodFromMap\FullMethodIdentifier;
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodByExactName;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;

/* @var $map MethodSelectorMap */

//add the \DateTime::modify method selector under the "date_time.modify" key
$map->addSelector(
    //key within the map
    "date_time.modify",
    //full method identifier
    new FullMethodIdentifier(
        //source of the class which contains this method
        new ExactClassPath(\DateTime::class),
        //actual method selector
        new MethodByExactName("modify")
    )
);
使用映射中的方法选择器
<?php
use lukaszmakuch\Haringo\MethodSelector\Impl\MethodFromMap;

//method selector stored under the "date_time.modify" key
$methodSelector = new MethodFromMap("date_time.modify");

参数选择器

按位置选择参数

从左(从索引 0)选择一个参数。

<?php
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByPosition;

//second parameter of some method
$paramSelector = new ParamByPosition(1);

按精确名称选择参数

通过它的精确名称选择一个参数。

<?php
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByExactName;

//parameter called "name"
$paramSelector = new ParamByExactName("name");

从映射中选择参数

允许将任何完整的参数标识符分配给一个字符串键。完整的参数标识符是完整的类路径来源和一些方法选择器和参数选择器。

由于每个完整标识符都包含一个类路径来源和一个方法选择器,因此可以在同一键下映射两个参数,它们将表示不同方法(甚至是不同类)中的不同参数。

在您重命名或移动参数或方法时,键保持不变,因此先前序列化的构建计划不会过时。

为了使 Haringo 能够从映射中获取参数选择器,它必须与映射一起构建

添加新的参数选择器
<?php
use lukaszmakuch\Haringo\ParamSelectorMatcher\Impl\ParamFromMapMatcher\ParamSelectorMap;
use lukaszmakuch\Haringo\ParamSelectorMatcher\Impl\ParamFromMapMatcher\FullParamIdentifier;
use lukaszmakuch\Haringo\MethodSelectorMatcher\Impl\MethodFromMap\FullMethodIdentifier;
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamByPosition;

/* @var $map ParamSelectorMap */

//add the first parameter of the \DateTime::modify method
//under the "date_time.modify.first_param" key
$map->addSelector(
    //key of the mapped selector
    "date_time.modify.first_param",
    //full identifier of the parameter
    new FullParamIdentifier(
        //full identifier of the method
        new FullMethodIdentifier(
            //class source
            new ExactClassPath(\DateTime::class),
            //method selector
            new MethodByExactName("modify")
        ),
        //actual parameter selector
        new ParamByPosition(0)
    )
);
使用映射中的参数选择器
<?php
use lukaszmakuch\Haringo\ParamSelector\Impl\ParamFromMap;

//parameter selector stored under the "date_time.modify.first_param" key
$paramSelector = new ParamFromMap("date_time.modify.first_param");

值源

标量值

表示如:布尔值、整数、浮点数、字符串的值。

<?php
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;

//string "Hello Haringo!"
$valueSource = new ScalarValue("Hello Haringo!");

//integer 42
$valueSource = new ScalarValue(42);

//float 36.6
$valueSource = new ScalarValue(36.6);

//boolean true
$valueSource = new ScalarValue(true);

数组值

表示其他ValueSource对象的数组。其他来源也可以是ArrayValue对象。键可以是整数或字符串。

<?php
use lukaszmakuch\Haringo\ValueSource\Impl\ArrayValue;
use lukaszmakuch\Haringo\ValueSource\Impl\ScalarValue;

//[123 => "one two three", "anotherKey" => true]
$valueSource = new ArrayValue();
$valueSource->addValue(123, new ScalarValue("one two three"));
$valueSource->addValue("anotherKey", new ScalarValue(true));

BuildPlanResultValue

表示基于给定的BuildPlan对象构建出的值。它可以用于创建复杂复合体的构建计划。

<?php
use lukaszmakuch\Haringo\BuildPlan\Impl\NewInstanceBuildPlan;
use lukaszmakuch\Haringo\ClassSource\Impl\ExactClassPath;
use lukaszmakuch\Haringo\ValueSource\Impl\BuildPlanResultValue;

//create a build plan of a new \DateTime
$plan = new NewInstanceBuildPlan();
$plan->setClassSource(new ExactClassPath(\DateTime::class));

//create a value source based on this plan
$valueSource = new BuildPlanResultValue($plan);

与构建计划一起工作

根据构建计划构建对象

要获取产品,请调用带有BuildPlanbuildObjectBasedOn方法

<?php
use lukaszmakuch\Haringo\Haringo;
use lukaszmakuch\Haringo\BuildPlan\BuildPlan;
use lukaszmakuch\Haringo\Exception\UnableToBuild;

/* @var $haringo Haringo */
/* @var $buildPlan BuildPlan */
try {
    $buitObject = $haringo->buildObjectBasedOn($buildPlan);
} catch (UnableToBuild $e) {
    //it was impossible to build an object based on the given build plan
}

构建计划的序列化

有两种方法可以实现构建计划的序列化和反序列化

  • Haringo::serializeBuildPlan(BuildPlan): String
  • Haringo::deserializeBuildPlan(String): BuildPlan
<?php
use lukaszmakuch\Haringo\Haringo;
use lukaszmakuch\Haringo\BuildPlan\BuildPlan;
use lukaszmakuch\Haringo\Exception\UnableToDeserialize;
use lukaszmakuch\Haringo\Exception\UnableToSerialize;

/* @var $haringo Haringo */
/* @var $buildPlan BuildPlan */
try {
    $serializedBuildPlan = $haringo->serializeBuildPlan(buildPlan);
    $deserializedBuildPlan = $haringo->deserializeBuildPlan(serializedBuildPlan);
} catch (UnableToSerialize $e) {
    //it was impossible to serialize this build plan
} catch (UnableToDeserialize $e) {
    //it was impossible to deserialize this build plan
}

通过构建对象获取构建计划

可以确定用于构建某些对象的构建计划是什么

<?php
use lukaszmakuch\Haringo\Haringo;
use lukaszmakuch\Haringo\Exception\BuildPlanNotFound;
use lukaszmakuch\Haringo\Exception\UnableToBuild;
use lukaszmakuch\Haringo\BuildPlan\BuildPlan;

/* @var $haringo Haringo */
/* @var $buildPlan BuildPlan */
try {
    $buitObject = $haringo->buildObjectBasedOn($buildPlan);
    $fetchedBuildPlan = $haringo->getBuildPlanUsedToBuild(buitObject);
} catch (UnableToBuild $e) {
    //it was impossible to build an object based on the given build plan
} catch (BuildPlanNotFound $e) {
    //it was impossible to fetch the build plan used to build the given object
}

扩展Haringo

Haringo易于扩展!

ValueSourceExtension

可以添加对全新的ValueSource的支持。你需要做的是实现\lukaszmakuch\Haringo\Builder\Extension\ValueSourceExtension接口,并将其添加到HaringoBuilder中,如《构建Haringo》章节所述。有关更多详细信息,请参阅该接口的文档。