monospice/spicy-identifiers

一种解析和操作标识符名称(如动态方法名称)的简单方法。

0.1.0 2017-01-29 09:00 UTC

This package is auto-updated.

Last update: 2024-09-21 20:06:04 UTC


README

Build Status

一种解析和操作标识符名称(如动态方法名称)的简单方法。

此包改进了与动态标识符名称(如动态访问的方法、变量/属性)一起工作的体验。它还简化了标识符大小写格式(如camelCasesnake_case)之间的转换。

例如,假设我们有一个包含配置指令数组的配置文件和一个消费这些指令的类。如果我们用snake_case编写配置键,而类使用camelCase来定义相应的setter方法,我们可以使用这个包来更轻松地连接两者。下面是一个示例。

注意:当前稳定版本仅包括支持与动态方法和函数一起工作。对类、变量和属性的支待正在开发中。

简单示例

开发者经常使用PHP的魔法方法来动态重载类方法以实现更灵活的功能。 此包可以简化处理动态方法调用所需的工作

<?php

// Call a dynamic method:
$someClass->callSomeDynamicMethod('some argument');

// The dynamic method call is handled by the __call() magic method of a class.
// $methodCalled is the name of the dynamic method: "callSomeDynamicMethod"
public function __call($methodCalled, array $arguments)
{
    // Use Spicy Identifiers to work with the dynamic method
    $method = DynamicMethod::parse($methodCalled);

    // Check if the method name starts and ends with certain strings
    if ($method->startsWith('call') && $method->endsWith('Method')) {
        $method->replace(0, 'get')->replace(3, 'Variable');
        // The dynamic method name is now "getSomeDynamicVariable"
    }

    // Alert the developer if they called a method that doesn't exist
    $method->throwExceptionIfMissingOn($this);

    // Check that the method includes the word "Dynamic" in the name,
    // then call the method represented by the name and return that value
    if ($method->has(2) && $method[2] === 'Dynamic') {
        return $method->callOn($this, $arguments);
    }
}

考虑另一个示例,其中一个类消费存储为数组的配置指令。配置键以snake_case编写,但类使用camelCase来定义相应的setter方法。此包使加载配置变得容易

<?php

$config = [
    'config_directive_1' => 'some value',
    'config_directive_2' => 'some value',
    ...
];

class UsesConfig
{
    public function __construct(array $config)
    {
        foreach ($config as $option => $value) {
            DynamicMethod::parse($option)
                ->prepend('set')
                ->throwExceptionIfMissingOn($this, 'Invalid option: ' . $option)
                ->callOn($this, [ $value ]); // calls setConfigDirectiveN()
        }
    }

    public function setConfigDirective1($value) { ... }

    public function setConfigDirective2($value) { ... }

    ...
}

安装

$ composer require monospice/spicy-identifiers

我们需要确保导入我们想要使用的类

use Monospice\SpicyIdentifiers\DynamicVariable;
use Monospice\SpicyIdentifiers\DynamicMethod;
use Monospice\SpicyIdentifiers\DynamicFunction;
use Monospice\SpicyIdentifiers\DynamicClass;
use Monospice\SpicyIdentifiers\Tools\CaseFormat;

此包自动安装了相关Spicy Identifier Tools包中的类,这些类位于命名空间Monospice\SpicyIdentifiers\Tools中。

标识符类型

此包提供不同类来处理各种类型的标识符

  • DynamicIdentifier:一个通用类,用于操作标识符名称,但不提供其他功能
  • DynamicMethod:提供方法和默认值,以加速与类方法的协作过程
  • DynamicFunction:提供方法和默认值,以加速与标准函数的协作过程
  • DynamicVariable:提供方法和默认值,以加速与变量的协作过程
  • DynamicClass:提供方法和默认值,以加速与类的协作过程

解析标识符名称

要开始使用标识符字符串(如方法名称),请使用包的其中一个工厂方法将其解析成对象

$method = DynamicMethod::parse('aMethodName');
$function = DynamicFunction::parse('a_function_name');
$variable = DynamicVariable::parse('aVariableName');
$class = DynamicClass::parse('AClassName');

$method->parts(); // array('a', 'Method', 'Name')

::parse()工厂方法使用包中每个类的标识符类型表示的默认大小写格式。要解析特定格式的标识符,请使用相应的解析方法

$method = DynamicMethod::parseFromUnderscore('a_method_name');
$method->parts(); // array('a', 'method', 'name')

除了::parse()之外,我们还可以使用此包中任何DynamicIdentifier子类的以下工厂方法来解析特定格式的标识符

  • parseFromCamelCase() - 标识符如:anIdentifierName
  • parseFromCamelCaseExtended() - 标识符如:änÏdentifierNáme
  • parseFromUnderscore() - 标识符如:an_identifier_name
  • parseFromSnakeCase() - parseFromUnderscore()的别名
  • parseFromHyphen() - 标识符如:an-identifier-name
  • parseFromMixedCase() - 标识符如:aMixed_case-identifier

有关标识符格式和大小写混合或扩展ASCII标识符的更多信息,请参阅下文。

加载标识符

有时我们可能希望使用此包中类的动态功能,但不需要将标识符字符串解析为其组成部分。在这种情况下,我们可以通过使用::from()工厂方法来简单地创建一个标识符字符串的实例,从而避免调用解析器来提高性能。

$method = DynamicMethod::from('aMethodName');
$method->parts(); // array('aMethodName')

$returnValue = $method->callOn($this);

同样,在某些情况下,我们可能已经知道或拥有我们想要使用的标识符部分集合。我们可以调用::fromParts()工厂方法来为表示的标识符创建一个实例

$method = DynamicMethod::fromParts(['a', 'method', 'name']);
$method->parts(); // array('a', 'method', 'name')

$returnValue = $method->callOn($this);

标识符操作

解析标识符后,我们可以使用此包来操作这些部分。以下示例将使用此DynamicIdentifier实例

$identifier = DynamicIdentifier::parse('anIdentifierName');

在任何时候,我们都可以检索当前标识符名称

name() - 获取整个标识符名称的字符串表示

$identifier->name();                    // "anIdentifierName"

或者,我们可以将动态标识符实例转换为字符串

echo $identifier;                       // "anIdentifierName"

获取标识符部分数据

parts() - 获取标识符部分名称数组

$identifier->parts();                   // ['an', 'Identifier', 'Name']
$identifier->toArray();                 // an alias for parts()

part() - 获取指定标识符部分的字符串值

$identifer->part(1);                    // "Identifier"

或者,使用数组访问来获取值

$identifier[1];                         // "Identifier"

请注意,部分数组是从零开始的,因此第一个部分对应于索引0

first() - 获取第一个标识符部分的值

$identifier->first();                   // "an"

last() - 获取最后一个标识符部分的值

$identifier->last();                    // "Name"

keys() - 获取标识符部分索引数组

$identifier->keys();                    // [0, 1, 2]

我们可以将一个字符串传递给keys()方法以获取匹配值的索引数组

$identifier->keys('Name');              // [2]

请注意,默认情况下keys()执行不区分大小写的比较。为了匹配确切的大小写,将第二个参数设置为true

$identifier->keys('NAME', true);        // [ ]

getNumParts() - 获取标识符部分的数量

$identifier->getNumParts();             // 3
$identifier->count();                   // an alias for getNumParts()

或者,使用PHP的count()函数获取标识符部分的数量

count($identifier);                     // 3

检查标识符部分

has() - 检查标识符是否在指定索引处包含部分

$identifier->has(1);                    // true

也可以使用数组访问来做到这一点

isset($identifier[1]);                  // true

startsWith() - 检查标识符是否以指定的字符串开头

$identifier->startsWith('an');          // true
$identifier->startsWith('identifier');  // false

endsWith() - 检查标识符是否以指定的字符串结尾

$identifier->endsWith('name');          // true
$identifier->endsWith('identifier');    // false

请注意,默认情况下startsWith()endsWith()执行不区分大小写的比较。为了匹配确切的大小写,将第二个参数设置为true

$identifier->endsWith('NAME', true);    // false

添加部分

append() - 将部分添加到标识符的末尾

$identifier->append('last');            // "anIdentifierNameLast"
$identifier->push('last');              // alias for append()

或者,使用数组访问将部分推送到标识符的末尾

$identifier[] = 'last';

prepend() - 将部分添加到标识符的开头

$identifier->prepend('first');          // "firstAnIdentifierName"

insert() - 在标识符的指定位置添加部分

$identifier->insert(1, 'insert');       // "anInsertIdentifierName"

删除部分

pop() - 从标识符的末尾删除部分

$identifier->pop();                     // "anIdentifier"

shift() - 从标识符的开头删除部分

$identifier->shift();                   // "identifierName"

remove() - 从标识符的指定位置删除部分

$identifier->remove(1);                 // "anName"

替换部分

replace() - 替换标识符指定位置的部分

$identifier->replace(2, 'String');      // "anIdentifierString"

或者,使用数组访问替换指定索引的部分

$identifier[2] = 'String';              // "anIdentifierString"

合并部分

合并部分不会更改输出字符串,但会组合内部数组的部分。这对于其他操作很有用。

mergeRange() - 合并指定位置之间的标识符部分

$identifier = DynamicIdentifier::parse('anIdentifierName');

echo $identifier->mergeRange(1, 2);     // "anIdentifierName"
$identifier->parts();                   // array(
                                        //     0 => "an",
                                        //     1 => "IdentifierName"
                                        // )

如果没有指定结束位置,则合并起始位置之后的任何剩余部分。

$identifier->mergeRange(0)->parts();    // array(
                                        //     0 => "anIdentifierName"
                                        // )

动态方法

DynamicMethod类为处理与解析的标识符名称对应的底层类方法添加了功能。

$method = DynamicMethod::parse('someMethod');

existsOn() - 检查表示的方法是否存在于给定的类上下文中

$method->existsOn('Namespace\SomeClass');
$method->existsOn(SomeClass::class);
$method->existsOn($someInstance);
$method->existsOn($this);

callOn() - 在给定上下文中调用由解析的方法名称表示的方法

$returnValue = $method->callOn($someInstance);
$returnValue = $method->callOn($someInstance, ['arg1', 'arg2']);
$returnValue = $method->callOn($this, ['arg1', 'arg2']);

// Static Methods
$returnValue = $method->callOn('Namespace\SomeClass');
$returnValue = $method->callOn('Namespace\SomeClass', ['arg1']);

callFromScopeOn() - 从该上下文的范围中调用由解析的方法名称表示的方法

此方法与 callOn() 类似,但它允许访问 DynamicMethod 实例无法直接调用的私有和受保护方法。callFromScopeOn() 适用于 DynamicMethod 在一个可能直接访问其私有和受保护成员的类内部使用的场景。在选择此方法之前应仔细考虑这种使用,并且始终使用 callOn() 调用公共方法。

$returnValue = $method->callFromScopeOn($someInstance);
$returnValue = $method->callFromScopeOn($this, ['arg1', 'arg2']);

// Static Methods
$returnValue = $method->callFromScopeOn('Namespace\SomeClass');
$returnValue = $method->callFromScopeOn('Namespace\SomeClass', ['arg1']);

forwardStaticCallTo() - 将调用转发到由解析的方法名称表示的静态方法,用于后期静态绑定

$returnValue = $method->forwardStaticCallTo('Namespace\SomeClass');
$returnValue = $method->forwardStaticCallTo('SomeClass', ['arg1', 'arg2']);

throwException() - 抛出 BadMethodCallException。默认异常信息假定异常是由于方法不存在而抛出的

$method->throwException();

可以在第一个参数中指定异常信息

$method->throwException('A custom exception message');

throwExceptionIfMissingOn() - 如果给定上下文中不存在该方法,则抛出 BadMethodCallException

$method->throwExceptionIfMissingOn($someObject);
$method->throwExceptionIfMissingOn('Namespace\SomeClass');

可以在第二个参数中指定异常信息

$method->throwExceptionIfMissingOn($someObject, 'A custom exception message');

动态函数

DynamicFunction 类为使用与解析标识符名称对应的底层标准函数添加了功能。

$function = DynamicFunction::parse('some_function');

exists() - 检查表示的函数是否存在

$function->exists();

call() - 调用由解析的函数名称表示的函数

$returnValue = $function->call();
$returnValue = $function->call(['arg1']);

throwException() - 抛出 BadFunctionCallException。默认异常信息假定异常是由于函数不存在而抛出的

$function->throwException();

可以在第一个参数中指定异常信息

$function->throwException('A custom exception message');

throwExceptionIfMissing() - 如果函数不存在,则抛出 BadFunctionCallException

$function->throwExceptionIfMissing();

可以在第一个参数中指定异常信息

$function->throwExceptionIfMissing('A custom exception message');

更改动态标识符类型

我们可以从本包中的任何动态标识符类中获得特定的标识符类型。例如,开发者可以使用与相同标识符名称的 DynamicVariable 来获取 DynamicMethod 的表示,但具有特定于变量的方法

$method = DynamicMethod::parse('anIdentifierName');
$method->name();                   // 'anIdentifierName'

$variable = $method->toVariable();
get_class($variable);              // Monospice\SpicyIdentifiers\DynamicVariable
$variable->name();                 // 'anIdentifierName'
$variable->value();                // the value of the corresponding variable

请注意,此功能不会强制转换原始对象,而是返回对应类的新的实例。因此,如果您计划在代码中稍后使用转换后的实例,请记住将返回的对象赋给变量。这种设计鼓励为每种类型使用适当的变量名称。

可用的转换有

$method = $identifier->toMethod();      // DynamicMethod
$variable = $identifier->toVariable();  // DynamicVariable
$class = $identifier->toClass();        // DynamicClass
$function = $identifier->toFunction();  // DynamicFunction

方法链

不返回输出值的函数可以链接使用

$returnValue = DynamicMethod::parse('aDynamicMethod')
    ->append('last')
    ->mergeRange(1, 2)
    ->callOn($this);

标识符大小写格式

每个类都使用默认的大小写格式来解析和输出标识符。这些格式是在 Tools\CaseFormat 类上设置的常量。

有关支持的格式更多信息,请参阅此包自动包含的 Spicy Identifier Tools 包。

要覆盖此默认格式,请使用专用方法之一解析标识符,并/或明确设置输出格式

// parse and output with the default case format (camel case)
$identifier = DynamicIdentifier::parse('identifierName');

// parse with an explicit case format, output with the default format
$identifier = DynamicIdentifier::parseFromUnderscore('identifier_name');

// parse with an explicit format, and set an explicit output format
$identifier = DynamicIdentifier::parseFromUnderscore('identifier_name')
    ->setOutputFormat(CaseFormat::UPPER_CAMEL_CASE);

标识符名称中的缩写

有时标识符名称包含缩写,如 JavaScript 中的 XMLHttpRequest 中的 XML。解析方法保留这些缩写

$method = DynamicMethod::parse('XMLHttpRequest')
$method->parts();   // array('XML', 'Http', 'Request');

但是,除非我们设置保留缩写的输出格式,否则输出方法将不会保留这些缩写

$method->name();    // "xmlHttpRequest"

$method
    ->setOutputFormat(CaseFormat::CAMEL_CASE_WITH_ACRONYMS)
    ->name();       // "XMLHttpRequest"

此行为在转换或标准化标识符名称时提供了灵活性。

大小写混合格式的标识符

尽管在实践中不建议使用大小写混合的标识符,但可以使用 ::parseFromMixedCase() 方法解析包含多个大小写的标识符

// parseFromMixedCase($identiferString, $arrayOfCaseFormatsToParse);

DynamicIdentifier::parseFromMixedCase('aMixed_case-identifier', [
    CaseFormat::CAMEL_CASE,
    CaseFormat::UNDERSCORE,
    CaseFormat::HYPHEN,
])
    ->parts(); // array('a', 'Mixed', 'case', 'identifier');

此包不提供以混合格式输出标识符的支持。任何输出方法都将使用默认格式格式化输出字符串,除非明确指定(见上一节)。

扩展 ASCII 标识符(实验性)

PHP支持在标识符名称中使用扩展ASCII字符。例如,字符ä

// From the php.net manual:
$täyte = 'mansikka';    // valid; 'ä' is (Extended) ASCII 228.

在通过下划线或连字符解析标识符时,这些字符没有效果。然而,驼峰式标识符可能包含由扩展ASCII字符分隔的单词,例如änÏdentifierNáme

Spicy Identifiers包提供了一种实验性的方法来解析这些标识符

DynamicIdentifier::parseFromCamelCaseExtended('änÏdentifierNáme')
    ->parts(); // array('än', 'Ïdentifier', 'Náme');

此方法的稳定性取决于源文件的字符编码以及环境语言和编码设置。作为最佳实践,应避免在标识符名称中使用扩展ASCII字符。

更多信息,请访问PHP手册:https://php.ac.cn/manual/en/language.variables.basics.php

测试

Spicy Identifiers包使用PHPSpec来测试对象行为。

$ vendor/bin/phpspec run

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件