├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── autoload.php
├── composer.json
├── phpcs.xml
├── phpmd.xml
├── phpstan.neon
├── phpunit.xml
├── src
├── Alias
│ ├── Alias.php
│ ├── AliasInterface.php
│ ├── AliasIterator.php
│ ├── AliasIteratorInterface.php
│ ├── Finder
│ │ ├── AliasFinder.php
│ │ └── AliasFinderInterface.php
│ └── Renderer
│ │ ├── AliasRenderer.php
│ │ └── AliasRendererInterface.php
├── Formatter
│ ├── CamelCaseFormatter.php
│ ├── CamelCaseTrait.php
│ ├── FunctionFormatter.php
│ ├── FunctionFormatterInterface.php
│ └── NamespacePrefixFormatter.php
└── Hasher
│ ├── FunctionHasherInterface.php
│ └── Md5FunctionHasher.php
└── tests
├── Alias
├── AliasIteratorTest.php
├── AliasTest.php
├── Finder
│ └── AliasFinderTest.php
└── Renderer
│ └── AliasRendererTest.php
├── CreateFunctionTrait.php
├── Fixture
└── functions.php
├── Formatter
├── CamelCaseFormatterTest.php
├── CamelCaseTraitTest.php
├── FunctionFormatterTest.php
└── NamespacePrefixFormatterTest.php
├── Hasher
└── Md5FunctionHasherTest.php
└── Integration
└── InternalFunctionAliasTest.php
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | coverage_clover: tests/Logs/clover.xml
2 | json_path: tests/Logs/coveralls-upload.json
3 | service_name: travis-ci
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | composer.lock
3 | vendor/
4 | aliases.*.php
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | dist: trusty
3 | php:
4 | - '7.1'
5 | - '7.2'
6 | install:
7 | - composer update
8 | script:
9 | - ./vendor/bin/phpunit --coverage-clover ./tests/Logs/clover.xml
10 | after_script:
11 | - php vendor/bin/php-coveralls -v
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🐪💼 PHP Camel Caser
2 |
3 | [](https://travis-ci.org/DivineOmega/php-camel-caser)
4 | [](https://coveralls.io/github/DivineOmega/php-camel-caser?branch=master)
5 | [](https://github.styleci.io/repos/147511192)
6 | [](https://packagist.org/packages/divineomega/php-camel-caser/stats)
7 |
8 | This package lets you use built-in PHP functions in camel case.
9 |
10 | ## Installation
11 |
12 |
13 | PHP Camel Caser can be easily installed using Composer. Just run the following command from the root of your project.
14 |
15 | ```
16 | composer require divineomega/php-camel-caser
17 | ```
18 |
19 | If you have never used the Composer dependency manager before, head to the [Composer website](https://getcomposer.org/) for more information on how to get started.
20 |
21 | ## Usage
22 |
23 | After installing PHP Camel Caser, the new functions are available straight away.
24 |
25 | Some example usage is shown below.
26 |
27 | ```php
28 | require_once __DIR__.'/vendor/autoload.php';
29 |
30 | strReplace('c', 'b', 'cat'); // bat
31 | strWordCount("Hello world!"); // 2
32 | inArray('Picard', ['Picard', 'Janeway']); // true
33 |
34 | // and so on...
35 | ```
36 |
37 |
--------------------------------------------------------------------------------
/autoload.php:
--------------------------------------------------------------------------------
1 | fwrite($render);
54 | }
55 | }
56 |
57 | try {
58 | require_once $storage;
59 | } catch (Throwable $exception) {
60 | unlink($storage);
61 |
62 | throw new RuntimeException(
63 | 'Failed to register aliases for internal functions!',
64 | 0,
65 | $exception
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "divineomega/php-camel-caser",
3 | "description": "Lets you use built-in PHP functions in camel case",
4 | "type": "library",
5 | "license": "LGPL-3.0-only",
6 | "authors": [
7 | {
8 | "name": "Jordan Hall",
9 | "email": "jordan@hall05.co.uk"
10 | },
11 | {
12 | "name": "Jan-Marten de Boer",
13 | "email": "github@johmanx.com"
14 | }
15 | ],
16 | "require": {
17 | "php": "^7.1",
18 | "ext-json": ">=1.5"
19 | },
20 | "require-dev": {
21 | "mediact/testing-suite": "@stable",
22 | "kint-php/kint": "^2.2",
23 | "php-coveralls/php-coveralls": "^2.0"
24 | },
25 | "autoload": {
26 | "psr-4": {
27 | "DivineOmega\\CamelCaser\\": "src"
28 | },
29 | "files": [
30 | "autoload.php"
31 | ]
32 | },
33 | "autoload-dev": {
34 | "psr-4": {
35 | "DivineOmega\\CamelCaser\\Tests\\": "tests"
36 | },
37 | "files": [
38 | "tests/Fixture/functions.php"
39 | ]
40 | },
41 | "extra": {
42 | "grumphp": {
43 | "config-default-path": "vendor/mediact/testing-suite/config/default/grumphp.yml"
44 | }
45 | },
46 | "archive": {
47 | "exclude": [
48 | "/.gitignore",
49 | "/phpunit.xml",
50 | "/phpmd.xml",
51 | "/phpstan.neon",
52 | "/phpcs.xml",
53 | "/tests"
54 | ]
55 | },
56 | "config": {
57 | "sort-packages": true
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | PHPCS
8 |
9 |
10 |
11 |
12 | tests/Fixture/functions.php
13 |
14 |
15 |
--------------------------------------------------------------------------------
/phpmd.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | PHPMD
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | excludes_analyse:
3 | # - %rootDir%/../../../path/to/exclude/*
4 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | tests
7 |
8 |
9 | tests/Integration
10 |
11 |
12 | tests/Alias
13 | tests/Hasher
14 | tests/Formatter
15 |
16 |
17 |
18 |
19 | src
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/Alias/Alias.php:
--------------------------------------------------------------------------------
1 | original = $original;
31 | $this->alias = $alias;
32 | $this->reflection = $reflection;
33 | }
34 |
35 | /**
36 | * Get the original function name.
37 | *
38 | * @return string
39 | */
40 | public function getOriginal(): string
41 | {
42 | return $this->original;
43 | }
44 |
45 | /**
46 | * Get the function alias.
47 | *
48 | * @return string
49 | */
50 | public function getAlias(): string
51 | {
52 | return $this->alias;
53 | }
54 |
55 | /**
56 | * Get a reflection of the original function.
57 | *
58 | * @return ReflectionFunctionAbstract
59 | */
60 | public function getReflection(): ReflectionFunctionAbstract
61 | {
62 | return $this->reflection;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Alias/AliasInterface.php:
--------------------------------------------------------------------------------
1 | originalFormatter = $originalFormatter;
30 | $this->aliasFormatter = $aliasFormatter;
31 | }
32 |
33 | /**
34 | * Find aliases for the given list of functions.
35 | *
36 | * @param ReflectionFunctionAbstract ...$functions
37 | *
38 | * @return AliasIteratorInterface
39 | */
40 | public function __invoke(
41 | ReflectionFunctionAbstract ...$functions
42 | ): AliasIteratorInterface {
43 | return new AliasIterator(
44 | ...array_values(
45 | array_reduce(
46 | $functions,
47 | function (
48 | array $carry,
49 | ReflectionFunctionAbstract $function
50 | ): array {
51 | $original = $this->originalFormatter->__invoke($function);
52 | $alias = $this->aliasFormatter->__invoke($function);
53 |
54 | if ($alias !== $original
55 | && preg_match('/.*?([^\\\\]+)$/', $alias)
56 | && function_exists($original)
57 | && !function_exists($alias)
58 | ) {
59 | $carry[$alias] = new Alias(
60 | $original,
61 | $alias,
62 | $function
63 | );
64 | }
65 |
66 | return $carry;
67 | },
68 | []
69 | )
70 | )
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Alias/Finder/AliasFinderInterface.php:
--------------------------------------------------------------------------------
1 | groupNamespaces(...$aliases) as $namespace => $group) {
28 | $code .= sprintf('namespace %s {', $namespace).PHP_EOL;
29 |
30 | foreach ($group as $alias) {
31 | try {
32 | $code .= $this->renderFunction($alias);
33 | } catch (RuntimeException $exception) {
34 | // Skip the erroneous function.
35 | continue;
36 | }
37 | }
38 |
39 | $code .= '}'.PHP_EOL;
40 | }
41 |
42 | return $code;
43 | }
44 |
45 | /**
46 | * Group the given list of alias by their namespace.
47 | *
48 | * @param AliasInterface ...$aliases
49 | *
50 | * @return AliasInterface[][]
51 | */
52 | private function groupNamespaces(AliasInterface ...$aliases): array
53 | {
54 | return array_reduce(
55 | $aliases,
56 | function (array $carry, AliasInterface $alias): array {
57 | $carry[$alias->getReflection()->getNamespaceName()][] = $alias;
58 |
59 | return $carry;
60 | },
61 | []
62 | );
63 | }
64 |
65 | /**
66 | * Render a function for the given alias.
67 | *
68 | * @param AliasInterface $alias
69 | *
70 | * @return string
71 | */
72 | private function renderFunction(AliasInterface $alias): string
73 | {
74 | return sprintf(
75 | <<<'FUNCTION'
76 | if (!function_exists('\%2$s')) {
77 | /**%3$s
78 | * @see %1$s
79 | * %5$s
80 | */
81 | function %4$s%2$s(%6$s)
82 | {
83 | return %1$s(...func_get_args());
84 | }
85 | }
86 |
87 | FUNCTION
88 | ,
89 | $alias->getOriginal(),
90 | preg_replace(
91 | '/.*?([^\\\\]+)$/',
92 | '$1',
93 | $alias->getAlias()
94 | ),
95 | $this->renderParameterDoc(
96 | ' * ',
97 | ...$alias
98 | ->getReflection()
99 | ->getParameters()
100 | ),
101 | $alias->getReflection()->returnsReference() ? '&' : '',
102 | $this->renderReturnTypeDoc(
103 | $alias->getReflection()->getReturnType()
104 | ),
105 | $this->renderParameters(
106 | str_repeat(' ', 8),
107 | ...$alias->getReflection()->getParameters()
108 | )
109 | ).PHP_EOL;
110 | }
111 |
112 | /**
113 | * Render a list of parameters for the doc block.
114 | *
115 | * @param string $prefix
116 | * @param ReflectionParameter ...$parameters
117 | *
118 | * @return string
119 | */
120 | private function renderParameterDoc(
121 | string $prefix,
122 | ReflectionParameter ...$parameters
123 | ): string {
124 | return array_reduce(
125 | $parameters,
126 | function (
127 | string $carry,
128 | ReflectionParameter $parameter
129 | ) use ($prefix): string {
130 | return $carry.PHP_EOL.$prefix.sprintf(
131 | '@param %s %s$%s',
132 | $this->renderTypeDoc($parameter->getType()),
133 | $parameter->isVariadic() ? '...' : '',
134 | static::camelCase(
135 | $parameter->getName()
136 | )
137 | );
138 | },
139 | ''
140 | );
141 | }
142 |
143 | /**
144 | * Render the type.
145 | *
146 | * @param null|ReflectionType $type
147 | *
148 | * @return string
149 | */
150 | private function renderTypeDoc(?ReflectionType $type): string
151 | {
152 | $types = [];
153 |
154 | if ($type === null) {
155 | $types[] = 'mixed';
156 | } else {
157 | if ($type->allowsNull()) {
158 | $types[] = 'null';
159 | }
160 |
161 | $types[] = (string) $type;
162 | }
163 |
164 | return implode('|', $types);
165 | }
166 |
167 | /**
168 | * Render a return type for the doc block.
169 | *
170 | * @param null|ReflectionType $returnType
171 | *
172 | * @return string
173 | */
174 | private function renderReturnTypeDoc(
175 | ?ReflectionType $returnType
176 | ): string {
177 | return sprintf(
178 | '@return %s',
179 | $this->renderTypeDoc($returnType)
180 | );
181 | }
182 |
183 | /**
184 | * Render a list of parameters.
185 | *
186 | * @param string $indent
187 | * @param ReflectionParameter ...$parameters
188 | *
189 | * @throws RuntimeException When duplicate parameters are encountered.
190 | *
191 | * @return string
192 | */
193 | private function renderParameters(
194 | string $indent,
195 | ReflectionParameter ...$parameters
196 | ): string {
197 | $list = array_reduce(
198 | $parameters,
199 | function (
200 | array $carry,
201 | ReflectionParameter $parameter
202 | ) use ($indent): array {
203 | $name = static::camelCase(
204 | $parameter->getName()
205 | );
206 |
207 | $value = $this->getDefaultParameterValue($parameter);
208 |
209 | // @see https://bugs.php.net/bug.php?id=76918
210 | if (array_key_exists($name, $carry)) {
211 | throw new RuntimeException(
212 | sprintf(
213 | 'Encountered duplicate parameter "%s" for %s.',
214 | $name,
215 | $parameter->getDeclaringFunction()->getName()
216 | )
217 | );
218 | }
219 |
220 | $carry[$name] = $indent.' '.trim(
221 | sprintf(
222 | '%1$s %2$s$%3$s%4$s',
223 | $parameter->hasType()
224 | ? sprintf(
225 | '%s%s',
226 | $parameter->getType()->allowsNull() ? '?' : '',
227 | $parameter->getType()
228 | )
229 | : '',
230 | $parameter->isVariadic() ? '...' : '',
231 | $name,
232 | empty($value) ? '' : sprintf(' = %s', $value)
233 | )
234 | );
235 |
236 | return $carry;
237 | },
238 | []
239 | );
240 |
241 | $list = implode(','.PHP_EOL, $list);
242 |
243 | if (strlen($list) > 0) {
244 | $list = PHP_EOL.$list.PHP_EOL.$indent;
245 | }
246 |
247 | return $list;
248 | }
249 |
250 | /**
251 | * Get the default value of the given parameter.
252 | *
253 | * @param ReflectionParameter $parameter
254 | *
255 | * @return string
256 | */
257 | private function getDefaultParameterValue(
258 | ReflectionParameter $parameter
259 | ): string {
260 | // Variadic parameter cannot have a default value.
261 | // This needs to be checked before isOptional.
262 | if ($parameter->isVariadic()) {
263 | return '';
264 | }
265 |
266 | // Cannot determine default value for internal functions.
267 | if ($parameter->getDeclaringFunction()->isInternal()) {
268 | return '';
269 | }
270 |
271 | try {
272 | if ($parameter->isDefaultValueConstant()) {
273 | return $parameter->getDefaultValueConstantName();
274 | }
275 |
276 | if ($parameter->isDefaultValueAvailable()) {
277 | return json_encode($parameter->getDefaultValue());
278 | }
279 | } catch (Throwable $exception) {
280 | return '';
281 | }
282 |
283 | // @codeCoverageIgnoreStart
284 | // No scenario known for this state.
285 | return '';
286 | // @codeCoverageIgnoreEnd
287 | }
288 | }
289 |
--------------------------------------------------------------------------------
/src/Alias/Renderer/AliasRendererInterface.php:
--------------------------------------------------------------------------------
1 | getShortName()
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Formatter/CamelCaseTrait.php:
--------------------------------------------------------------------------------
1 | getShortName();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Formatter/FunctionFormatterInterface.php:
--------------------------------------------------------------------------------
1 | formatter = $formatter;
20 | }
21 |
22 | /**
23 | * Format the given function reflection into a string.
24 | *
25 | * @param ReflectionFunctionAbstract $function
26 | *
27 | * @return string
28 | */
29 | public function __invoke(ReflectionFunctionAbstract $function): string
30 | {
31 | $formatted = $this->formatter->__invoke($function);
32 |
33 | return $function->inNamespace()
34 | ? sprintf(
35 | '\\%s\\%s',
36 | $function->getNamespaceName(),
37 | $formatted
38 | )
39 | : sprintf('\\%s', $formatted);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Hasher/FunctionHasherInterface.php:
--------------------------------------------------------------------------------
1 | getName();
21 | },
22 | $functions
23 | );
24 |
25 | sort($names);
26 |
27 | return md5(
28 | implode(':', $names)
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Alias/AliasIteratorTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(AliasIterator::class, $subject);
29 | $this->assertEquals($aliases, iterator_to_array($subject));
30 | }
31 |
32 | /**
33 | * @return array
34 | */
35 | public function aliasProvider(): array
36 | {
37 | return [
38 | [],
39 | [$this->createMock(AliasInterface::class)],
40 | [
41 | $this->createMock(AliasInterface::class),
42 | $this->createMock(AliasInterface::class),
43 | $this->createMock(AliasInterface::class),
44 | ],
45 | ];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/Alias/AliasTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Alias::class, $subject);
31 | $this->assertEquals('in_array', $subject->getOriginal());
32 | $this->assertEquals('inArray', $subject->getAlias());
33 | $this->assertInstanceOf(
34 | \ReflectionFunctionAbstract::class,
35 | $subject->getReflection()
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/Alias/Finder/AliasFinderTest.php:
--------------------------------------------------------------------------------
1 | createMock(FunctionFormatterInterface::class);
39 |
40 | /** @var FunctionFormatterInterface|MockObject $aliasFormatter */
41 | $aliasFormatter = $this->createMock(FunctionFormatterInterface::class);
42 |
43 | $subject = new AliasFinder($originalFormatter, $aliasFormatter);
44 |
45 | $originalFormatter
46 | ->expects(self::exactly(count($functions)))
47 | ->method('__invoke')
48 | ->with(self::isInstanceOf(ReflectionFunctionAbstract::class))
49 | ->willReturnCallback(
50 | function (ReflectionFunctionAbstract $function): string {
51 | return $function->getName();
52 | }
53 | );
54 |
55 | $aliasFormatter
56 | ->expects(self::exactly(count($functions)))
57 | ->method('__invoke')
58 | ->with(self::isInstanceOf(ReflectionFunctionAbstract::class))
59 | ->willReturnCallback($aliasCallback);
60 |
61 | $result = $subject->__invoke(...$functions);
62 |
63 | $this->assertInstanceOf(AliasIteratorInterface::class, $result);
64 | $this->assertCount($expected, iterator_to_array($result));
65 | }
66 |
67 | /**
68 | * @return array
69 | */
70 | public function functionProvider(): array
71 | {
72 | return [
73 | [
74 | 0,
75 | function (): void {
76 | // Nothing to do.
77 | },
78 | ],
79 | [
80 | 0,
81 | function (ReflectionFunctionAbstract $function): string {
82 | return $function->getName();
83 | },
84 | $this->createFunction('in_array'),
85 | ],
86 | [
87 | 0,
88 | function (): string {
89 | return '';
90 | },
91 | $this->createFunction('in_array'),
92 | ],
93 | [
94 | 1,
95 | function (ReflectionFunctionAbstract $function): string {
96 | return md5($function->getName());
97 | },
98 | $this->createFunction('in_array'),
99 | ],
100 | [
101 | 1,
102 | function (ReflectionFunctionAbstract $function): string {
103 | return md5($function->getName());
104 | },
105 | $this->createFunction('in_array'),
106 | $this->createFunction('in_array'),
107 | $this->createFunction('in_array'),
108 | ],
109 | [
110 | 2,
111 | function (ReflectionFunctionAbstract $function): string {
112 | return md5($function->getName());
113 | },
114 | $this->createFunction('in_array'),
115 | $this->createFunction('array_key_exists'),
116 | $this->createFunction('in_array'),
117 | ],
118 | ];
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/tests/Alias/Renderer/AliasRendererTest.php:
--------------------------------------------------------------------------------
1 | assertStringStartsWith(
41 | '__invoke(...$aliases)
43 | );
44 | }
45 |
46 | /**
47 | * @param string $original
48 | * @param string $alias
49 | * @param ReflectionFunctionAbstract|null $reflection
50 | *
51 | * @return AliasInterface
52 | */
53 | public function createAlias(
54 | string $original,
55 | string $alias,
56 | ReflectionFunctionAbstract $reflection = null
57 | ): AliasInterface {
58 | /** @var AliasInterface|MockObject $mock */
59 | $mock = $this->createMock(AliasInterface::class);
60 |
61 | $mock
62 | ->expects(self::any())
63 | ->method('getOriginal')
64 | ->willReturn($original);
65 |
66 | $mock
67 | ->expects(self::any())
68 | ->method('getAlias')
69 | ->willReturn($alias);
70 |
71 | $mock
72 | ->expects(self::any())
73 | ->method('getReflection')
74 | ->willReturn(
75 | $reflection ?? new ReflectionFunction($original)
76 | );
77 |
78 | return $mock;
79 | }
80 |
81 | /**
82 | * @return array
83 | */
84 | public function aliasProvider(): array
85 | {
86 | return [
87 | [],
88 | array_map(
89 | function (string $original): AliasInterface {
90 | return $this->createAlias(
91 | $original,
92 | $this->camelCase($original)
93 | );
94 | },
95 | get_defined_functions(true)['internal'] ?? []
96 | ),
97 | array_map(
98 | function (string $original): AliasInterface {
99 | return $this->createAlias(
100 | $original,
101 | $this->camelCase($original)
102 | );
103 | },
104 | [
105 | '\DivineOmega\CamelCaser\Tests\Fixture\foo_default_constant_value',
106 | '\DivineOmega\CamelCaser\Tests\Fixture\bar_default_value_available',
107 | '\DivineOmega\CamelCaser\Tests\Fixture\baz_parameter_optional',
108 | '\DivineOmega\CamelCaser\Tests\Fixture\quz_parameter_required',
109 | '\DivineOmega\CamelCaser\Tests\Fixture\quu_parameter_nullable',
110 | ]
111 | ),
112 | ];
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/tests/CreateFunctionTrait.php:
--------------------------------------------------------------------------------
1 | createMock(ReflectionFunctionAbstract::class);
20 |
21 | $function
22 | ->expects(TestCase::any())
23 | ->method('getName')
24 | ->willReturn($name);
25 |
26 | $function
27 | ->expects(TestCase::any())
28 | ->method('getShortName')
29 | ->willReturn(
30 | preg_replace(
31 | '/.*?([^\\\\]+)$/',
32 | '$1',
33 | $name
34 | )
35 | );
36 |
37 | $function
38 | ->expects(TestCase::any())
39 | ->method('inNamespace')
40 | ->willReturn(
41 | strpos($name, '\\') !== false
42 | );
43 |
44 | $function
45 | ->expects(TestCase::any())
46 | ->method('getNamespaceName')
47 | ->willReturn(
48 | trim(
49 | preg_replace(
50 | '/(.*?)([^\\\\]+)$/',
51 | '$1',
52 | $name
53 | ),
54 | '\\'
55 | )
56 | );
57 |
58 | return $function;
59 | }
60 |
61 | /**
62 | * @param string|string[] $originalClassName
63 | *
64 | * @return MockObject
65 | */
66 | abstract protected function createMock($originalClassName): MockObject;
67 | }
68 |
--------------------------------------------------------------------------------
/tests/Fixture/functions.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, $subject->__invoke($function));
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function functionProvider(): array
40 | {
41 | return [
42 | [
43 | $this->createFunction('foo'),
44 | 'foo',
45 | ],
46 | [
47 | $this->createFunction('foo_bar'),
48 | 'fooBar',
49 | ],
50 | [
51 | $this->createFunction('fooBar'),
52 | 'fooBar',
53 | ],
54 | ];
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Formatter/CamelCaseTraitTest.php:
--------------------------------------------------------------------------------
1 | getMockForTrait(CamelCaseTrait::class);
27 |
28 | $this->assertEquals($expected, $subject::camelCase($original));
29 | }
30 |
31 | /**
32 | * @return array
33 | */
34 | public function stringProvider(): array
35 | {
36 | return [
37 | ['foo_bar', 'fooBar'],
38 | ['foo-bar', 'fooBar'],
39 | ['fooBar', 'fooBar'],
40 | ['foo-bar_bazQux', 'fooBarBazQux'],
41 | ['FOOBAR', 'fOOBAR'],
42 | ['FOO-BAR', 'fOOBAR'],
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/Formatter/FunctionFormatterTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, $subject->__invoke($function));
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function functionProvider(): array
40 | {
41 | return [
42 | [
43 | $this->createFunction('foo'),
44 | 'foo',
45 | ],
46 | [
47 | $this->createFunction('foo_bar'),
48 | 'foo_bar',
49 | ],
50 | [
51 | $this->createFunction('fooBar'),
52 | 'fooBar',
53 | ],
54 | ];
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Formatter/NamespacePrefixFormatterTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(
28 | NamespacePrefixFormatter::class,
29 | new NamespacePrefixFormatter(
30 | $this->createMock(FunctionFormatterInterface::class)
31 | )
32 | );
33 | }
34 |
35 | /**
36 | * @dataProvider functionProvider
37 | *
38 | * @param ReflectionFunctionAbstract $function
39 | * @param string $expected
40 | *
41 | * @return void
42 | *
43 | * @covers ::__invoke
44 | */
45 | public function testInvoke(
46 | ReflectionFunctionAbstract $function,
47 | string $expected
48 | ): void {
49 | /** @var FunctionFormatterInterface|MockObject $formatter */
50 | $formatter = $this->createMock(FunctionFormatterInterface::class);
51 | $subject = new NamespacePrefixFormatter($formatter);
52 |
53 | $formatter
54 | ->expects(self::once())
55 | ->method('__invoke')
56 | ->with(self::isInstanceOf(ReflectionFunctionAbstract::class))
57 | ->willReturnCallback(
58 | function (ReflectionFunctionAbstract $function): string {
59 | return strtoupper($function->getShortName());
60 | }
61 | );
62 |
63 | $this->assertEquals($expected, $subject->__invoke($function));
64 | }
65 |
66 | /**
67 | * @return array
68 | */
69 | public function functionProvider(): array
70 | {
71 | return [
72 | [
73 | $this->createFunction('foo'),
74 | '\FOO',
75 | ],
76 | [
77 | $this->createFunction('Foo\Bar\baz'),
78 | '\Foo\Bar\BAZ',
79 | ],
80 | ];
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tests/Hasher/Md5FunctionHasherTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, $subject->__invoke(...$functions));
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function functionProvider(): array
40 | {
41 | return [
42 | ['d41d8cd98f00b204e9800998ecf8427e'],
43 | [
44 | 'acbd18db4cc2f85cedef654fccc4a4d8',
45 | $this->createFunction('foo'),
46 | ],
47 | [
48 | 'c0c27d0b79228a06ef81a8111136edea',
49 | $this->createFunction('foo'),
50 | $this->createFunction('bar'),
51 | $this->createFunction('baz'),
52 | ],
53 | [
54 | 'c0c27d0b79228a06ef81a8111136edea',
55 | $this->createFunction('baz'),
56 | $this->createFunction('bar'),
57 | $this->createFunction('foo'),
58 | ],
59 | ];
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/Integration/InternalFunctionAliasTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(
33 | function_exists($original),
34 | 'The original function must always exist.'
35 | );
36 |
37 | $this->assertTrue(
38 | function_exists($expected),
39 | 'The alias function must always exist.'
40 | );
41 |
42 | $originalReflection = new ReflectionFunction($original);
43 | $this->assertTrue(
44 | $originalReflection->isInternal(),
45 | 'Original function must be an internal function.'
46 | );
47 |
48 | $aliasReflection = new ReflectionFunction($expected);
49 | $this->assertTrue(
50 | $aliasReflection->isUserDefined(),
51 | 'Alias function must be user defined.'
52 | );
53 | }
54 |
55 | /**
56 | * @return array
57 | */
58 | public function functionProvider(): array
59 | {
60 | return array_reduce(
61 | get_defined_functions(EXCLUDE_DISABLED_FUNCTIONS)['internal'] ?? [],
62 | function (array $carry, string $function): array {
63 | $alias = self::camelCase($function);
64 |
65 | if ($alias !== $function
66 | && !empty($alias)
67 | && $this->functionHasValidSignature($function)
68 | // In some cases, internal functions have internal aliases,
69 | // although not officially documented.
70 | && !$this->isInternal($alias)
71 | ) {
72 | $carry[] = [$function, $alias];
73 | }
74 |
75 | return $carry;
76 | },
77 | []
78 | );
79 | }
80 |
81 | /**
82 | * Determine whether the supplied function is an existing internal function.
83 | *
84 | * @param string $function
85 | *
86 | * @return bool
87 | */
88 | private function isInternal(string $function): bool
89 | {
90 | try {
91 | $reflection = new ReflectionFunction($function);
92 |
93 | return $reflection->isInternal();
94 | } catch (Throwable $exception) {
95 | return false;
96 | }
97 | }
98 |
99 | /**
100 | * Verify that the signature of the given function does not hold multiple
101 | * parameters with the same name.
102 | *
103 | * @see https://bugs.php.net/bug.php?id=76918
104 | *
105 | * @param string $function
106 | *
107 | * @return bool
108 | */
109 | private function functionHasValidSignature(string $function): bool
110 | {
111 | $reflection = new ReflectionFunction($function);
112 | $parameters = array_map(
113 | function (ReflectionParameter $parameter): string {
114 | return $parameter->getName();
115 | },
116 | $reflection->getParameters()
117 | );
118 |
119 | return $parameters === array_unique($parameters);
120 | }
121 | }
122 |
--------------------------------------------------------------------------------