From e04307b527ac9b997a62e8a2b7ef11444ed75b1f Mon Sep 17 00:00:00 2001 From: jawira Date: Mon, 13 Jun 2022 21:44:19 +0200 Subject: [PATCH 1/4] feat: Bump minimum PHP version to 8.0 --- .editorconfig | 9 ++ .github/workflows/qa.yaml | 4 +- README.md | 39 +++---- composer.json | 2 +- docs/case-mapping.md | 6 +- src/CaseConverter.php | 1 - src/CaseConverterException.php | 5 +- src/CaseConverterInterface.php | 1 - src/Convert.php | 167 +++++++--------------------- src/Glue/AdaCase.php | 2 - src/Glue/CamelCase.php | 2 - src/Glue/CobolCase.php | 2 - src/Glue/DashGluer.php | 3 +- src/Glue/DotNotation.php | 3 +- src/Glue/Gluer.php | 74 +++--------- src/Glue/SpaceGluer.php | 3 +- src/Glue/UnderscoreGluer.php | 3 +- src/Glue/UppercaseGluer.php | 3 +- src/Split/DashSplitter.php | 3 +- src/Split/DotSplitter.php | 4 +- src/Split/SpaceSplitter.php | 3 +- src/Split/Splitter.php | 9 +- src/Split/UnderscoreSplitter.php | 2 +- src/Split/UppercaseSplitter.php | 2 +- tests/phpunit/CaseConverterTest.php | 1 - tests/phpunit/ConvertTest.php | 57 +++++----- 26 files changed, 141 insertions(+), 269 deletions(-) diff --git a/.editorconfig b/.editorconfig index 7c6ebd5..4e244fc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,6 +10,15 @@ ij_visual_guides = 80, 120 [*.php] indent_size = 4 +max_line_length = 120 +ij_php_align_multiline_chained_methods = true +ij_php_align_phpdoc_comments = true +ij_php_align_phpdoc_param_names = true +ij_php_align_assignments = true +ij_php_align_multiline_array_initializer_expression = true +ij_php_align_key_value_pairs = true +ij_php_align_match_arm_bodies = true +ij_php_phpdoc_use_fqcn = true [*.{yml, yaml, neon, xml, svg, json, js}] indent_size = 2 diff --git a/.github/workflows/qa.yaml b/.github/workflows/qa.yaml index e811477..e90af93 100644 --- a/.github/workflows/qa.yaml +++ b/.github/workflows/qa.yaml @@ -10,8 +10,8 @@ jobs: tests: strategy: matrix: - php: ['7.3','7.4','8.0','8.1'] - runs-on: ubuntu-20.04 + php: [ '7.4','8.0','8.1' ] + runs-on: ubuntu-22.04 steps: - name: Setup PHP diff --git a/README.md b/README.md index 306f9b7..cc1cd35 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,21 @@ Case converter Use this library to convert string between: -| Name | Method | Output example | -| ------------- | --------------- | ----------------- | -| 🐪 Camel case | `toCamel()` | `myNameIsBond` | -| 👨‍🏫 Pascal case | `toPascal()` | `MyNameIsBond` | -| 🐍 Snake case | `toSnake()` | `my_name_is_bond` | -| 👩‍🏫 Ada case | `toAda()` | `My_Name_Is_Bond` | -| Ⓜ️ Macro case | `toMacro()` | `MY_NAME_IS_BOND` | -| 🥙 Kebab case | `toKebab()` | `my-name-is-bond` | -| 🚂 Train case | `toTrain()` | `My-Name-Is-Bond` | -| 🏦 Cobol case | `toCobol()` | `MY-NAME-IS-BOND` | -| 🔡 Lower case | `toLower()` | `my name is bond` | -| 🔠 Upper case | `toUpper()` | `MY NAME IS BOND` | -| 📰 Title case | `toTitle()` | `My Name Is Bond` | -| ✍️ Sentence case | `toSentence()` | `My name is bond` | -| ⚙️ Dot notation | `toDot()` | `my.name.is.bond` | +| Name | Method | Output example | +|-------------------|----------------|-------------------| +| 🐪 Camel case | `toCamel()` | `myNameIsBond` | +| 👨‍🏫 Pascal case | `toPascal()` | `MyNameIsBond` | +| 🐍 Snake case | `toSnake()` | `my_name_is_bond` | +| 👩‍🏫 Ada case | `toAda()` | `My_Name_Is_Bond` | +| Ⓜ️ Macro case | `toMacro()` | `MY_NAME_IS_BOND` | +| 🥙 Kebab case | `toKebab()` | `my-name-is-bond` | +| 🚂 Train case | `toTrain()` | `My-Name-Is-Bond` | +| 🏦 Cobol case | `toCobol()` | `MY-NAME-IS-BOND` | +| 🔡 Lower case | `toLower()` | `my name is bond` | +| 🔠 Upper case | `toUpper()` | `MY NAME IS BOND` | +| 📰 Title case | `toTitle()` | `My Name Is Bond` | +| ✍️ Sentence case | `toSentence()` | `My name is bond` | +| ⚙️ Dot notation | `toDot()` | `my.name.is.bond` | Features: @@ -52,8 +52,8 @@ Of course you can explicitly set the format of input string: echo $hero->fromKebab()->toSnake(); // output: john_connor ``` -You can also use the [provided factory][factory] to instantiate `Convert` class. A list of [all public methods] is also -available. +You can also use the [provided factory][factory] to instantiate `Convert` class. +A list of [all public methods] is also available. i18n ---- @@ -74,7 +74,8 @@ $rus = new Convert('ОЧЕНЬ_ПРИЯТНО'); echo $rus->toCamel(); // output: оченьПриятно ``` -`case-converter` is compatible with _Simple Case-Mapping_ and _Full Case-Mapping_. +`case-converter` is compatible with _Simple Case-Mapping_ and _Full +Case-Mapping_. [Learn more about Case-Mapping][Case-Mapping]. Installation @@ -94,7 +95,7 @@ Contributing - If you liked this project, ⭐ star it on GitHub. [![GitHub Repo stars](https://img.shields.io/github/stars/jawira/case-converter?style=social)](https://github.com/jawira/case-converter) -- Or follow me on Twitter. +- Or follow me on Twitter. [![Twitter Follow](https://img.shields.io/twitter/follow/jawira?style=social)](https://twitter.com/jawira) License diff --git a/composer.json b/composer.json index 5b54a02..6643d8d 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ } ], "require": { - "php": "^7.1 || ^8.0", + "php": ">=7.4", "ext-mbstring": "*" }, "require-dev": { diff --git a/docs/case-mapping.md b/docs/case-mapping.md index 80325b6..2ecf5fc 100644 --- a/docs/case-mapping.md +++ b/docs/case-mapping.md @@ -4,8 +4,8 @@ Case-Mapping Introduction ------------ -> Case mapping or case conversion is a process whereby strings are converted -> to a particular form—uppercase, lowercase, or titlecase—possibly for display +> Case mapping or case conversion is a process whereby strings are converted +> to a particular form—uppercase, lowercase, or titlecase—possibly for display > to the user. PHP always performed _Simple Case-Mapping_, this is map one-to-one character @@ -28,7 +28,7 @@ Please note that _Full Case-Mapping_ is locale dependent: ```php // Turkish (requires appropriate locale) -$tur = new Convert('istambul'); +$tur = new Convert('istambul'); echo $tur->toTrain(); // output: İstanbul ``` diff --git a/src/CaseConverter.php b/src/CaseConverter.php index 41ac1dd..cfba5d8 100644 --- a/src/CaseConverter.php +++ b/src/CaseConverter.php @@ -7,7 +7,6 @@ * * Factory class which returns a Convert object. * - * @package Jawira\CaseConverter * @author Jawira Portugal */ class CaseConverter implements CaseConverterInterface diff --git a/src/CaseConverterException.php b/src/CaseConverterException.php index 30bf9bc..a50abd5 100644 --- a/src/CaseConverterException.php +++ b/src/CaseConverterException.php @@ -2,9 +2,8 @@ namespace Jawira\CaseConverter; -use Exception; +use RuntimeException; -class CaseConverterException extends Exception +class CaseConverterException extends RuntimeException { - } diff --git a/src/CaseConverterInterface.php b/src/CaseConverterInterface.php index ad57d9a..58df113 100644 --- a/src/CaseConverterInterface.php +++ b/src/CaseConverterInterface.php @@ -5,7 +5,6 @@ /** * Interface CaseConverterInterface * - * @package Jawira\CaseConverter * @author Jawira Portugal */ interface CaseConverterInterface diff --git a/src/Convert.php b/src/Convert.php index 7bf5051..a41eb5a 100644 --- a/src/Convert.php +++ b/src/Convert.php @@ -26,7 +26,6 @@ use Jawira\CaseConverter\Split\UnderscoreSplitter; use Jawira\CaseConverter\Split\UppercaseSplitter; use function is_subclass_of; -use function mb_strpos; use function preg_match; /** @@ -77,25 +76,17 @@ * * @see https://softwareengineering.stackexchange.com/questions/322413/bothered-by-an-unknown-letter-case-name * @see http://www.unicode.org/charts/case/ - * @package Jawira\CaseConverter * @author Jawira Portugal */ class Convert { - /** - * @var string Input string to convert - */ - protected $source; + /** @var string Input string to convert */ + protected string $source; - /** - * @var string[] Words extracted from input string - */ - protected $words; + /** @var string[] Words extracted from input string */ + protected array $words; - /** - * @var bool - */ - protected $forceSimpleCaseMapping; + protected bool $forceSimpleCaseMapping; /** * Constructor method @@ -114,17 +105,17 @@ public function __construct(string $source) /** * Handle `to*` methods and `from*` methods * - * @param string $methodName - * @param mixed[] $arguments + * @param string $methodName + * @param mixed[] $arguments * * @return string|\Jawira\CaseConverter\Convert * @throws \Jawira\CaseConverter\CaseConverterException */ public function __call($methodName, $arguments) { - if (0 === mb_strpos($methodName, 'from')) { + if (str_starts_with($methodName, 'from')) { $result = $this->handleSplitterMethod($methodName); - } elseif (0 === mb_strpos($methodName, 'to')) { + } elseif (str_starts_with($methodName, 'to')) { $result = $this->handleGluerMethod($methodName); } else { throw new CaseConverterException("Unknown method: $methodName"); @@ -136,7 +127,6 @@ public function __call($methodName, $arguments) /** * Auto-detect naming convention * - * @return \Jawira\CaseConverter\Convert * @throws \Jawira\CaseConverter\CaseConverterException */ public function fromAuto(): self @@ -187,46 +177,19 @@ public function forceSimpleCaseMapping(): self * * @param string $input String to be analysed * - * @throws \Jawira\CaseConverter\CaseConverterException * @return \Jawira\CaseConverter\Split\Splitter + * @throws \Jawira\CaseConverter\CaseConverterException */ protected function analyse(string $input): Splitter { - switch (true) { - case $this->contains($input, UnderscoreGluer::DELIMITER): - $splittingStrategy = new UnderscoreSplitter($input); - break; - case $this->contains($input, DashGluer::DELIMITER): - $splittingStrategy = new DashSplitter($input); - break; - case $this->contains($input, SpaceGluer::DELIMITER): - $splittingStrategy = new SpaceSplitter($input); - break; - case $this->contains($input, DotNotation::DELIMITER): - $splittingStrategy = new DotSplitter($input); - break; - case $this->isUppercaseWord($input): - $splittingStrategy = new UnderscoreSplitter($input); - break; - default: - $splittingStrategy = new UppercaseSplitter($input); - break; - } - - return $splittingStrategy; - } - - /** - * Return true if $needle is found in $input string - * - * @param string $input String where the search is performed - * @param string $needle Needle - * - * @return bool - */ - protected function contains(string $input, string $needle): bool - { - return is_int(mb_strpos($input, $needle)); + return match (true) { + str_contains($input, UnderscoreGluer::DELIMITER) => new UnderscoreSplitter($input), + str_contains($input, DashGluer::DELIMITER) => new DashSplitter($input), + str_contains($input, SpaceGluer::DELIMITER) => new SpaceSplitter($input), + str_contains($input, DotNotation::DELIMITER) => new DotSplitter($input), + $this->isUppercaseWord($input) => new UnderscoreSplitter($input), + default => new UppercaseSplitter($input), + }; } /** @@ -279,33 +242,14 @@ protected function extractWords(Splitter $splitter): self */ protected function handleSplitterMethod(string $methodName): self { - switch ($methodName) { - case 'fromCamel': - case 'fromPascal': - $splitterName = UppercaseSplitter::class; - break; - case 'fromSnake': - case 'fromAda': - case 'fromMacro': - $splitterName = UnderscoreSplitter::class; - break; - case 'fromKebab': - case 'fromTrain': - case 'fromCobol': - $splitterName = DashSplitter::class; - break; - case 'fromLower': - case 'fromUpper': - case 'fromTitle': - case 'fromSentence': - $splitterName = SpaceSplitter::class; - break; - case 'fromDot': - $splitterName = DotSplitter::class; - break; - default: - throw new CaseConverterException("Unknown method: $methodName"); - } + $splitterName = match ($methodName) { + 'fromCamel', 'fromPascal' => UppercaseSplitter::class, + 'fromSnake', 'fromAda', 'fromMacro' => UnderscoreSplitter::class, + 'fromKebab', 'fromTrain', 'fromCobol' => DashSplitter::class, + 'fromLower', 'fromUpper', 'fromTitle', 'fromSentence' => SpaceSplitter::class, + 'fromDot' => DotSplitter::class, + default => throw new CaseConverterException("Unknown method: $methodName"), + }; $splitter = $this->createSplitter($splitterName, $this->source); $this->extractWords($splitter); @@ -336,49 +280,22 @@ protected function createSplitter(string $className, string $source): Splitter */ protected function handleGluerMethod(string $methodName): string { - switch ($methodName) { - case 'toAda': - $className = AdaCase::class; - break; - case 'toCamel': - $className = CamelCase::class; - break; - case 'toCobol': - $className = CobolCase::class; - break; - case 'toKebab': - $className = KebabCase::class; - break; - case 'toLower': - $className = LowerCase::class; - break; - case 'toMacro': - $className = MacroCase::class; - break; - case 'toPascal': - $className = PascalCase::class; - break; - case 'toSentence': - $className = SentenceCase::class; - break; - case 'toSnake': - $className = SnakeCase::class; - break; - case 'toTitle': - $className = TitleCase::class; - break; - case 'toTrain': - $className = TrainCase::class; - break; - case 'toUpper': - $className = UpperCase::class; - break; - case 'toDot': - $className = DotNotation::class; - break; - default: - throw new CaseConverterException("Unknown method: $methodName"); - } + $className = match ($methodName) { + 'toAda' => AdaCase::class, + 'toCamel' => CamelCase::class, + 'toCobol' => CobolCase::class, + 'toKebab' => KebabCase::class, + 'toLower' => LowerCase::class, + 'toMacro' => MacroCase::class, + 'toPascal' => PascalCase::class, + 'toSentence' => SentenceCase::class, + 'toSnake' => SnakeCase::class, + 'toTitle' => TitleCase::class, + 'toTrain' => TrainCase::class, + 'toUpper' => UpperCase::class, + 'toDot' => DotNotation::class, + default => throw new CaseConverterException("Unknown method: $methodName"), + }; $gluer = $this->createGluer($className, $this->words, $this->forceSimpleCaseMapping); diff --git a/src/Glue/AdaCase.php b/src/Glue/AdaCase.php index 857088f..801cdef 100644 --- a/src/Glue/AdaCase.php +++ b/src/Glue/AdaCase.php @@ -6,8 +6,6 @@ * Class AdaCase * * Outputs string in _Ada case_ format: This_Is_Ada_Case - * - * @package Jawira\CaseConverter\Glue */ class AdaCase extends UnderscoreGluer { diff --git a/src/Glue/CamelCase.php b/src/Glue/CamelCase.php index 9f13b8a..95b7d0b 100644 --- a/src/Glue/CamelCase.php +++ b/src/Glue/CamelCase.php @@ -6,8 +6,6 @@ * Class CamelCase * * Outputs string in _Camel case_ format: thisIsCamelCase - * - * @package Jawira\CaseConverter\Glue */ class CamelCase extends UppercaseGluer { diff --git a/src/Glue/CobolCase.php b/src/Glue/CobolCase.php index 160079a..da0da17 100644 --- a/src/Glue/CobolCase.php +++ b/src/Glue/CobolCase.php @@ -6,8 +6,6 @@ * Class CobolCase * * Outputs string in _Cobol case_ format: THIS-IS-COBOL-CASE - * - * @package Jawira\CaseConverter\Glue */ class CobolCase extends DashGluer { diff --git a/src/Glue/DashGluer.php b/src/Glue/DashGluer.php index 86270a2..adbfeab 100644 --- a/src/Glue/DashGluer.php +++ b/src/Glue/DashGluer.php @@ -4,5 +4,6 @@ abstract class DashGluer extends Gluer { - const DELIMITER = '-'; + /** @internal */ + public const DELIMITER = '-'; } diff --git a/src/Glue/DotNotation.php b/src/Glue/DotNotation.php index 46a4246..c31e8ba 100644 --- a/src/Glue/DotNotation.php +++ b/src/Glue/DotNotation.php @@ -9,7 +9,8 @@ */ class DotNotation extends Gluer { - const DELIMITER = '.'; + /** @internal */ + public const DELIMITER = '.'; /** * Format detected words in _dot notation_ diff --git a/src/Glue/Gluer.php b/src/Glue/Gluer.php index 7a973c8..9c58de0 100644 --- a/src/Glue/Gluer.php +++ b/src/Glue/Gluer.php @@ -12,10 +12,9 @@ /** * Class Gluer * - * A Gluer sub-class allow to export an array of words in a single string + * A Gluer subclass allow to export an array of words in a single string * * @author Jawira Portugal - * @package Jawira\CaseConverter\Glue */ abstract class Gluer { @@ -24,27 +23,27 @@ abstract class Gluer * * This value should never change. */ - const ENCODING = 'UTF-8'; + protected const ENCODING = 'UTF-8'; /** * @var string[] Words extracted from input string */ - protected $words; + protected array $words; /** * @var int MB_CASE_LOWER or MB_CASE_LOWER_SIMPLE */ - protected $lowerCase; + protected int $lowerCase; /** * @var int MB_CASE_UPPER or MB_CASE_UPPER_SIMPLE */ - protected $upperCase; + protected int $upperCase; /** * @var int MB_CASE_TITLE or MB_CASE_TITLE_SIMPLE */ - protected $titleCase; + protected int $titleCase; /** @@ -56,13 +55,9 @@ abstract class Gluer final public function __construct(array $words, bool $forceSimpleCaseMapping) { $this->words = $words; - $this->lowerCase = MB_CASE_LOWER; - $this->upperCase = MB_CASE_UPPER; - $this->titleCase = MB_CASE_TITLE; - - if ($forceSimpleCaseMapping) { - $this->setSimpleCaseMappingConstants(); - } + $this->lowerCase = $forceSimpleCaseMapping ? MB_CASE_LOWER_SIMPLE : MB_CASE_LOWER; + $this->upperCase = $forceSimpleCaseMapping ? MB_CASE_UPPER_SIMPLE : MB_CASE_UPPER; + $this->titleCase = $forceSimpleCaseMapping ? MB_CASE_TITLE_SIMPLE : MB_CASE_TITLE; } /** @@ -72,55 +67,16 @@ final public function __construct(array $words, bool $forceSimpleCaseMapping) */ abstract public function glue(): string; - /** - * Use new constants if available - * - * Since PHP 7.3, new constants are used to specify _simple case mapping_. This method handles these new constants. - * - * Usually you would use: - * - * - MB_CASE_LOWER - * - MB_CASE_UPPER - * - MB_CASE_TITLE - * - * But PHP 7.3 introduced new constants: - * - * - MB_CASE_LOWER_SIMPLE - * - MB_CASE_UPPER_SIMPLE - * - MB_CASE_TITLE_SIMPLE - * - * @see https://www.php.net/manual/en/migration73.constants.php#migration73.constants.mbstring - * @see https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.mbstring.case-mapping-folding - */ - protected function setSimpleCaseMappingConstants(): self - { - /** @var int $lowerCase */ - $lowerCase = defined('\MB_CASE_LOWER_SIMPLE') ? constant('\MB_CASE_LOWER_SIMPLE') : MB_CASE_LOWER; - /** @var int $upperCase */ - $upperCase = defined('\MB_CASE_UPPER_SIMPLE') ? constant('\MB_CASE_UPPER_SIMPLE') : MB_CASE_UPPER; - /** @var int $titleCase */ - $titleCase = defined('\MB_CASE_TITLE_SIMPLE') ? constant('\MB_CASE_TITLE_SIMPLE') : MB_CASE_TITLE; - - $this->lowerCase = $lowerCase; - $this->upperCase = $upperCase; - $this->titleCase = $titleCase; - - return $this; - } - /** * Implode self::$words array using $glue. * - * @param string $glue Character to glue words. Even if is assumed your are using underscore or dash - * character, this method should be capable to use any character as glue. - * @param int $wordsMode The mode of the conversion. It should be one of `Gluer::$lowerCase`, - * `Gluer::$upperCase` or `Gluer::$titleCase`. - * @param int $firstWordMode Sometimes first word requires special treatment. It should be one of - * `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. + * @param string $glue Character to glue words. Even if is assumed your are using underscore or dash character, this method should be capable to use any character as glue. + * @param int $wordsMode The mode of the conversion. It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. + * @param null|int $firstWordMode Sometimes first word requires special treatment. It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. * * @return string */ - protected function glueUsingRules(string $glue, int $wordsMode, int $firstWordMode = null): string + protected function glueUsingRules(string $glue, int $wordsMode, ?int $firstWordMode = null): string { $convertedWords = $this->changeWordsCase($this->words, $wordsMode); @@ -149,9 +105,7 @@ protected function changeWordsCase(array $words, int $caseMode): array return mb_convert_case($word, $caseMode, self::ENCODING); }; - $convertedWords = array_map($closure, $words); - - return $convertedWords; + return array_map($closure, $words); } /** diff --git a/src/Glue/SpaceGluer.php b/src/Glue/SpaceGluer.php index b78bd78..44a978a 100644 --- a/src/Glue/SpaceGluer.php +++ b/src/Glue/SpaceGluer.php @@ -4,5 +4,6 @@ abstract class SpaceGluer extends Gluer { - const DELIMITER = ' '; + /** @internal */ + public const DELIMITER = ' '; } diff --git a/src/Glue/UnderscoreGluer.php b/src/Glue/UnderscoreGluer.php index 6304ae9..21046b0 100644 --- a/src/Glue/UnderscoreGluer.php +++ b/src/Glue/UnderscoreGluer.php @@ -4,5 +4,6 @@ abstract class UnderscoreGluer extends Gluer { - const DELIMITER = '_'; + /** @internal */ + public const DELIMITER = '_'; } diff --git a/src/Glue/UppercaseGluer.php b/src/Glue/UppercaseGluer.php index 6c26503..57bac00 100644 --- a/src/Glue/UppercaseGluer.php +++ b/src/Glue/UppercaseGluer.php @@ -4,5 +4,6 @@ abstract class UppercaseGluer extends Gluer { - const DELIMITER = ''; + /** @internal */ + public const DELIMITER = ''; } diff --git a/src/Split/DashSplitter.php b/src/Split/DashSplitter.php index 0a5be47..1f547b3 100644 --- a/src/Split/DashSplitter.php +++ b/src/Split/DashSplitter.php @@ -6,7 +6,8 @@ class DashSplitter extends Splitter { - const PATTERN = '#' . DashGluer::DELIMITER . '+#u'; + /** @internal */ + public const PATTERN = '#' . DashGluer::DELIMITER . '+#u'; /** * @return string[] diff --git a/src/Split/DotSplitter.php b/src/Split/DotSplitter.php index 0048d78..853c8ce 100644 --- a/src/Split/DotSplitter.php +++ b/src/Split/DotSplitter.php @@ -3,11 +3,11 @@ namespace Jawira\CaseConverter\Split; use Jawira\CaseConverter\Glue\DotNotation; -use Jawira\CaseConverter\Glue\SpaceGluer; class DotSplitter extends Splitter { - const PATTERN = '#\\' . DotNotation::DELIMITER . '+#u'; + /** @internal */ + public const PATTERN = '#\\' . DotNotation::DELIMITER . '+#u'; /** * @return string[] diff --git a/src/Split/SpaceSplitter.php b/src/Split/SpaceSplitter.php index d33e110..eaa6e32 100644 --- a/src/Split/SpaceSplitter.php +++ b/src/Split/SpaceSplitter.php @@ -6,7 +6,8 @@ class SpaceSplitter extends Splitter { - const PATTERN = '#' . SpaceGluer::DELIMITER . '+#u'; + /** @internal */ + public const PATTERN = '#' . SpaceGluer::DELIMITER . '+#u'; /** * @return string[] diff --git a/src/Split/Splitter.php b/src/Split/Splitter.php index e1cc20b..46119ba 100644 --- a/src/Split/Splitter.php +++ b/src/Split/Splitter.php @@ -7,17 +7,16 @@ /** * Class Splitter * - * A Splitter sub-class allows to read the words contained in a string + * A Splitter subclass allows to read the words contained in a string * * @author Jawira Portugal - * @package Jawira\CaseConverter\Split */ abstract class Splitter { /** * @var string Words extracted from input string */ - protected $inputString; + protected string $inputString; final public function __construct(string $inputString) { @@ -32,7 +31,7 @@ final public function __construct(string $inputString) abstract public function split(): array; /** - * This is an utility method, typically this method is used by to split a string based on pattern. + * This is a utility method, typically this method is used by to split a string based on pattern. * * @param string $inputString * @param string $pattern @@ -45,7 +44,7 @@ protected function splitUsingPattern(string $inputString, string $pattern): arra $words = preg_split($pattern, $inputString, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); if ($words === false) { - throw new CaseConverterException("Error while processing '{$this->inputString}'"); // @codeCoverageIgnore + throw new CaseConverterException("Error while processing $this->inputString"); // @codeCoverageIgnore } return $words; diff --git a/src/Split/UnderscoreSplitter.php b/src/Split/UnderscoreSplitter.php index 5524076..7d3d3bd 100644 --- a/src/Split/UnderscoreSplitter.php +++ b/src/Split/UnderscoreSplitter.php @@ -6,7 +6,7 @@ class UnderscoreSplitter extends Splitter { - const PATTERN = '#' . UnderscoreGluer::DELIMITER . '+#u'; + public const PATTERN = '#' . UnderscoreGluer::DELIMITER . '+#u'; /** * @return string[] diff --git a/src/Split/UppercaseSplitter.php b/src/Split/UppercaseSplitter.php index e8c8af6..3683268 100644 --- a/src/Split/UppercaseSplitter.php +++ b/src/Split/UppercaseSplitter.php @@ -5,7 +5,7 @@ class UppercaseSplitter extends Splitter { // language=PhpRegExp - const PATTERN = '#(?=\p{Lu}{1})#u'; + public const PATTERN = '#(?=\p{Lu}{1})#u'; /** * Splits $words using Uppercase letters. diff --git a/tests/phpunit/CaseConverterTest.php b/tests/phpunit/CaseConverterTest.php index 8e0f39e..adade50 100644 --- a/tests/phpunit/CaseConverterTest.php +++ b/tests/phpunit/CaseConverterTest.php @@ -11,7 +11,6 @@ class CaseConverterTest extends TestCase * * @covers \Jawira\CaseConverter\Convert::__construct * @covers \Jawira\CaseConverter\Convert::analyse - * @covers \Jawira\CaseConverter\Convert::contains * @covers \Jawira\CaseConverter\Convert::extractWords * @covers \Jawira\CaseConverter\Convert::fromAuto * @covers \Jawira\CaseConverter\Convert::getSource diff --git a/tests/phpunit/ConvertTest.php b/tests/phpunit/ConvertTest.php index 6804c26..b055e0d 100644 --- a/tests/phpunit/ConvertTest.php +++ b/tests/phpunit/ConvertTest.php @@ -85,19 +85,18 @@ public function isUppercaseWordProvider() * * @dataProvider analyseProvider * - * @param bool[] $containsReturnValues Return values for `contains()` * @param bool $isUppercaseWordReturn Return value for `isUppercaseWord()` * @param string $expected Expected result * @param string $input Input string * * @throws \ReflectionException */ - public function testAnalyse(array $containsReturnValues, bool $isUppercaseWordReturn, string $expected, string $input) + public function testAnalyse(bool $isUppercaseWordReturn, string $expected, string $input) { // Disabling constructor with one stub method $stub = $this->getMockBuilder(Convert::class) ->disableOriginalConstructor() - ->setMethods(['isUppercaseWord', 'contains']) + ->setMethods(['isUppercaseWord']) ->getMock(); // Configuring expectation @@ -105,10 +104,6 @@ public function testAnalyse(array $containsReturnValues, bool $isUppercaseWordRe ->method('isUppercaseWord') ->willReturn($isUppercaseWordReturn); - // Configuring expectation - $stub->expects($this->atLeastOnce()) - ->method('contains') - ->willReturnOnConsecutiveCalls(...$containsReturnValues); // Removing protected for analyse method $reflection = new ReflectionObject($stub); @@ -123,30 +118,30 @@ public function testAnalyse(array $containsReturnValues, bool $isUppercaseWordRe public function analyseProvider() { return [ - 'Underscore 1' => [[true, false, false, false], false, UnderscoreSplitter::class, 'hola_mundo'], - 'Underscore 2' => [[true, false, false, false], false, UnderscoreSplitter::class, 'HELLO_WORLD'], - 'Underscore 3' => [[false, false, false, false], true, UnderscoreSplitter::class, 'Ñ'], - 'Underscore 4' => [[false, false, false, false], true, UnderscoreSplitter::class, 'HELLO'], - 'Underscore 5' => [[true, false, false, false], false, UnderscoreSplitter::class, '_'], - 'Underscore 6' => [[true, false, false, false], false, UnderscoreSplitter::class, '_____'], - 'Uppercase 1' => [[false, false, false, false], false, UppercaseSplitter::class, ''], - 'Uppercase 2' => [[false, false, false, false], false, UppercaseSplitter::class, 'ñ'], - 'Uppercase 3' => [[false, false, false, false], false, UppercaseSplitter::class, 'one'], - 'Uppercase 4' => [[false, false, false, false], false, UppercaseSplitter::class, 'helloWorld'], - 'Dash 1' => [[false, true, false, false], false, DashSplitter::class, 'hello-World'], - 'Dash 2' => [[false, true, false, false], false, DashSplitter::class, 'my-name-is-bond'], - 'Dash 3' => [[false, true, false, false], false, DashSplitter::class, '-my-name-is-bond-'], - 'Dash 4' => [[false, true, false, false], false, DashSplitter::class, '-'], - 'Dash 5' => [[false, true, false, false], false, DashSplitter::class, '------'], - 'Space 1' => [[false, false, true, false], false, SpaceSplitter::class, 'Hola mundo'], - 'Space 2' => [[false, false, true, false], false, SpaceSplitter::class, 'Mi nombre es bond'], - 'Space 3' => [[false, false, true, false], false, SpaceSplitter::class, 'Formule courte spéciale été'], - 'Space 4' => [[false, false, true, false], false, SpaceSplitter::class, ' '], - 'Space 5' => [[false, false, true, false], false, SpaceSplitter::class, ' '], - 'Dot 1' => [[false, false, false, true], false, DotSplitter::class, 'one.two'], - 'Dot 2' => [[false, false, false, true], false, DotSplitter::class, '.hello.'], - 'Dot 3' => [[false, false, false, true], false, DotSplitter::class, '.'], - 'Dot 4' => [[false, false, false, true], false, DotSplitter::class, '........'], + 'Underscore 1' => [false, UnderscoreSplitter::class, 'hola_mundo'], + 'Underscore 2' => [false, UnderscoreSplitter::class, 'HELLO_WORLD'], + 'Underscore 3' => [true, UnderscoreSplitter::class, 'Ñ'], + 'Underscore 4' => [true, UnderscoreSplitter::class, 'HELLO'], + 'Underscore 5' => [false, UnderscoreSplitter::class, '_'], + 'Underscore 6' => [false, UnderscoreSplitter::class, '_____'], + 'Uppercase 1' => [false, UppercaseSplitter::class, ''], + 'Uppercase 2' => [false, UppercaseSplitter::class, 'ñ'], + 'Uppercase 3' => [false, UppercaseSplitter::class, 'one'], + 'Uppercase 4' => [false, UppercaseSplitter::class, 'helloWorld'], + 'Dash 1' => [false, DashSplitter::class, 'hello-World'], + 'Dash 2' => [false, DashSplitter::class, 'my-name-is-bond'], + 'Dash 3' => [false, DashSplitter::class, '-my-name-is-bond-'], + 'Dash 4' => [false, DashSplitter::class, '-'], + 'Dash 5' => [false, DashSplitter::class, '------'], + 'Space 1' => [false, SpaceSplitter::class, 'Hola mundo'], + 'Space 2' => [false, SpaceSplitter::class, 'Mi nombre es bond'], + 'Space 3' => [false, SpaceSplitter::class, 'Formule courte spéciale été'], + 'Space 4' => [false, SpaceSplitter::class, ' '], + 'Space 5' => [false, SpaceSplitter::class, ' '], + 'Dot 1' => [false, DotSplitter::class, 'one.two'], + 'Dot 2' => [false, DotSplitter::class, '.hello.'], + 'Dot 3' => [false, DotSplitter::class, '.'], + 'Dot 4' => [false, DotSplitter::class, '........'], ]; } From 1133e6c6ae8637596a5e426bc89afc32c6c316a1 Mon Sep 17 00:00:00 2001 From: jawira Date: Tue, 14 Jun 2022 22:26:35 +0200 Subject: [PATCH 2/4] fix: restore switch structures --- build.xml | 3 +- config/phpunit.xml | 17 ++++- src/Convert.php | 134 +++++++++++++++++++++++++++++---------- src/Glue/DotNotation.php | 2 - src/Glue/Gluer.php | 10 +-- src/Glue/KebabCase.php | 2 - 6 files changed, 119 insertions(+), 49 deletions(-) diff --git a/build.xml b/build.xml index d8d6e67..83cdb71 100644 --- a/build.xml +++ b/build.xml @@ -41,7 +41,7 @@ - + @@ -65,7 +65,6 @@ - diff --git a/config/phpunit.xml b/config/phpunit.xml index eb9adfb..75d958a 100644 --- a/config/phpunit.xml +++ b/config/phpunit.xml @@ -1,8 +1,19 @@ + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" + bootstrap="../vendor/autoload.php" + forceCoversAnnotation="true" + beStrictAboutCoversAnnotation="true" + beStrictAboutOutputDuringTests="true" + beStrictAboutTodoAnnotatedTests="true" + colors="true" + stopOnFailure="true" + stopOnRisky="true" + stopOnDefect="true" + stopOnWarning="true" + stopOnIncomplete="true" + stopOnSkipped="true" + stopOnError="true"> diff --git a/src/Convert.php b/src/Convert.php index a41eb5a..16b2cc2 100644 --- a/src/Convert.php +++ b/src/Convert.php @@ -111,11 +111,12 @@ public function __construct(string $source) * @return string|\Jawira\CaseConverter\Convert * @throws \Jawira\CaseConverter\CaseConverterException */ - public function __call($methodName, $arguments) + public function __call(string $methodName, array $arguments) { - if (str_starts_with($methodName, 'from')) { + $strStartsWith = static fn(string $haystack, string $needle): bool => 0 === mb_strpos($haystack, $needle); + if ($strStartsWith($methodName, 'from')) { $result = $this->handleSplitterMethod($methodName); - } elseif (str_starts_with($methodName, 'to')) { + } elseif ($strStartsWith($methodName, 'to')) { $result = $this->handleGluerMethod($methodName); } else { throw new CaseConverterException("Unknown method: $methodName"); @@ -182,14 +183,30 @@ public function forceSimpleCaseMapping(): self */ protected function analyse(string $input): Splitter { - return match (true) { - str_contains($input, UnderscoreGluer::DELIMITER) => new UnderscoreSplitter($input), - str_contains($input, DashGluer::DELIMITER) => new DashSplitter($input), - str_contains($input, SpaceGluer::DELIMITER) => new SpaceSplitter($input), - str_contains($input, DotNotation::DELIMITER) => new DotSplitter($input), - $this->isUppercaseWord($input) => new UnderscoreSplitter($input), - default => new UppercaseSplitter($input), - }; + $strContains = static fn(string $input, string $needle): bool => is_int(mb_strpos($input, $needle)); + + switch (true) { + case $strContains($input, UnderscoreGluer::DELIMITER): + $splittingStrategy = new UnderscoreSplitter($input); + break; + case $strContains($input, DashGluer::DELIMITER): + $splittingStrategy = new DashSplitter($input); + break; + case $strContains($input, SpaceGluer::DELIMITER): + $splittingStrategy = new SpaceSplitter($input); + break; + case $strContains($input, DotNotation::DELIMITER): + $splittingStrategy = new DotSplitter($input); + break; + case $this->isUppercaseWord($input): + $splittingStrategy = new UnderscoreSplitter($input); + break; + default: + $splittingStrategy = new UppercaseSplitter($input); + break; + } + + return $splittingStrategy; } /** @@ -242,14 +259,33 @@ protected function extractWords(Splitter $splitter): self */ protected function handleSplitterMethod(string $methodName): self { - $splitterName = match ($methodName) { - 'fromCamel', 'fromPascal' => UppercaseSplitter::class, - 'fromSnake', 'fromAda', 'fromMacro' => UnderscoreSplitter::class, - 'fromKebab', 'fromTrain', 'fromCobol' => DashSplitter::class, - 'fromLower', 'fromUpper', 'fromTitle', 'fromSentence' => SpaceSplitter::class, - 'fromDot' => DotSplitter::class, - default => throw new CaseConverterException("Unknown method: $methodName"), - }; + switch ($methodName) { + case 'fromCamel': + case 'fromPascal': + $splitterName = UppercaseSplitter::class; + break; + case 'fromSnake': + case 'fromAda': + case 'fromMacro': + $splitterName = UnderscoreSplitter::class; + break; + case 'fromKebab': + case 'fromTrain': + case 'fromCobol': + $splitterName = DashSplitter::class; + break; + case 'fromLower': + case 'fromUpper': + case 'fromTitle': + case 'fromSentence': + $splitterName = SpaceSplitter::class; + break; + case 'fromDot': + $splitterName = DotSplitter::class; + break; + default: + throw new CaseConverterException("Unknown method: $methodName"); + } $splitter = $this->createSplitter($splitterName, $this->source); $this->extractWords($splitter); @@ -280,22 +316,50 @@ protected function createSplitter(string $className, string $source): Splitter */ protected function handleGluerMethod(string $methodName): string { - $className = match ($methodName) { - 'toAda' => AdaCase::class, - 'toCamel' => CamelCase::class, - 'toCobol' => CobolCase::class, - 'toKebab' => KebabCase::class, - 'toLower' => LowerCase::class, - 'toMacro' => MacroCase::class, - 'toPascal' => PascalCase::class, - 'toSentence' => SentenceCase::class, - 'toSnake' => SnakeCase::class, - 'toTitle' => TitleCase::class, - 'toTrain' => TrainCase::class, - 'toUpper' => UpperCase::class, - 'toDot' => DotNotation::class, - default => throw new CaseConverterException("Unknown method: $methodName"), - }; + + switch ($methodName) { + case 'toAda': + $className = AdaCase::class; + break; + case 'toCamel': + $className = CamelCase::class; + break; + case 'toCobol': + $className = CobolCase::class; + break; + case 'toKebab': + $className = KebabCase::class; + break; + case 'toLower': + $className = LowerCase::class; + break; + case 'toMacro': + $className = MacroCase::class; + break; + case 'toPascal': + $className = PascalCase::class; + break; + case 'toSentence': + $className = SentenceCase::class; + break; + case 'toSnake': + $className = SnakeCase::class; + break; + case 'toTitle': + $className = TitleCase::class; + break; + case 'toTrain': + $className = TrainCase::class; + break; + case 'toUpper': + $className = UpperCase::class; + break; + case 'toDot': + $className = DotNotation::class; + break; + default: + throw new CaseConverterException("Unknown method: $methodName"); + } $gluer = $this->createGluer($className, $this->words, $this->forceSimpleCaseMapping); diff --git a/src/Glue/DotNotation.php b/src/Glue/DotNotation.php index c31e8ba..73515d7 100644 --- a/src/Glue/DotNotation.php +++ b/src/Glue/DotNotation.php @@ -4,8 +4,6 @@ /** * Class DotNotation - * - * @package Jawira\CaseConverter\Glue */ class DotNotation extends Gluer { diff --git a/src/Glue/Gluer.php b/src/Glue/Gluer.php index 9c58de0..7833ef9 100644 --- a/src/Glue/Gluer.php +++ b/src/Glue/Gluer.php @@ -70,11 +70,11 @@ abstract public function glue(): string; /** * Implode self::$words array using $glue. * - * @param string $glue Character to glue words. Even if is assumed your are using underscore or dash character, this method should be capable to use any character as glue. + * @param string $glue Character to glue words. Even if is assumed you are using underscore or dash character, this method should be capable to use any character as glue. * @param int $wordsMode The mode of the conversion. It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. * @param null|int $firstWordMode Sometimes first word requires special treatment. It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. * - * @return string + * @return string Converted words. */ protected function glueUsingRules(string $glue, int $wordsMode, ?int $firstWordMode = null): string { @@ -88,7 +88,7 @@ protected function glueUsingRules(string $glue, int $wordsMode, ?int $firstWordM } /** - * Changes the case of every $words' element + * Changes the case of every $words element * * @param string[] $words Words to modify * @param int $caseMode It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. @@ -101,7 +101,7 @@ protected function changeWordsCase(array $words, int $caseMode): array return $words; } - $closure = function (string $word) use ($caseMode): string { + $closure = static function (string $word) use ($caseMode): string { return mb_convert_case($word, $caseMode, self::ENCODING); }; @@ -109,7 +109,7 @@ protected function changeWordsCase(array $words, int $caseMode): array } /** - * Changes the case of first $words' element + * Changes the case of first $words element * * @param string[] $words Words to modify * @param int $caseMode It should be one of `Gluer::$lowerCase`, `Gluer::$upperCase` or `Gluer::$titleCase`. diff --git a/src/Glue/KebabCase.php b/src/Glue/KebabCase.php index e589f54..63d9921 100644 --- a/src/Glue/KebabCase.php +++ b/src/Glue/KebabCase.php @@ -6,8 +6,6 @@ * Class KebabCase * * Outputs string in _Cobol case_ format: this-is-kebab-case - * - * @package Jawira\CaseConverter\Glue */ class KebabCase extends DashGluer { From cbe93bd5fd50d77b963d7324ccf8c6c7e3aa4b8c Mon Sep 17 00:00:00 2001 From: jawira Date: Thu, 23 Jun 2022 22:07:04 +0200 Subject: [PATCH 3/4] test: Add full case-mapping tests --- tests/behat/case-converter.feature | 34 ++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/tests/behat/case-converter.feature b/tests/behat/case-converter.feature index 0863f7a..56fdd41 100644 --- a/tests/behat/case-converter.feature +++ b/tests/behat/case-converter.feature @@ -232,15 +232,17 @@ Feature: Convert Case | toDot | ОЧЕНЬ_ПРИЯТНО | очень.приятно | | toDot | Ես-հայերեն-չգիտեմ | ես.հայերեն.չգիտեմ | | toDot | XMLHttpRequest | x.m.l.http.request | + | toLower | IJSJE | ijsje | + | toUpper | ijsje | IJSJE | Scenario Outline: Convert a string to array Given CaseConverter class is instantiated with "" When I call "" - Then method should return array "" + Then method should return array "" Examples: - | method | input-string | output-array | + | method | input-string | output-string | | toArray | | [] | | toArray | a | [a] | | toArray | HugoPacoLuis | [Hugo;Paco;Luis] | @@ -251,11 +253,31 @@ Feature: Convert Case | toArray | red.green.blue | [red;green;blue] | - Scenario: Force simple case mapping - Given CaseConverter class is instantiated with "Straße" + Scenario Outline: Full case mapping (default behaviour) + Given CaseConverter class is instantiated with "" + When I call "" + Then method should return string "" + + Examples: + | method | input-string | output-string | + | toUpper | Straße | STRASSE | + | toLower | İstanbul | i̇stanbul | + | toUpper | ʼn | ʼN | + | toUpper | ffl | FFL | + + + Scenario Outline: Simple case mapping + Given CaseConverter class is instantiated with "" When I call "forceSimpleCaseMapping" - And I call "toMacro" - Then method should return string "STRAßE" + And I call "" + Then method should return string "" + + Examples: + | method | input-string | output-string | + | toUpper | Straße | STRAßE | + | toLower | İstanbul | istanbul | + | toUpper | ʼn | ʼn | + | toUpper | ffl | ffl | Scenario Outline: Using numbers in input strings From cc39caf8ecf1ece7b77d5942a5ea821bb46c74db Mon Sep 17 00:00:00 2001 From: jawira Date: Thu, 23 Jun 2022 22:08:55 +0200 Subject: [PATCH 4/4] docs: Update case-mapping documentation --- .editorconfig | 1 + LICENSE.md | 25 ++++----- build.xml | 1 + docs/case-mapping.md | 128 +++++++++++++++++++++++-------------------- 4 files changed, 83 insertions(+), 72 deletions(-) diff --git a/.editorconfig b/.editorconfig index 4e244fc..9d45fb3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -27,3 +27,4 @@ indent_size = 2 trim_trailing_whitespace = false indent_size = 2 max_line_length = 80 +ij_markdown_wrap_text_if_long = true diff --git a/LICENSE.md b/LICENSE.md index 34bbf35..a291a82 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,22 +1,21 @@ The MIT License (MIT) ===================== -Copyright © 2015-2019 Jawira Portugal +Copyright © 2015-2022 Jawira Portugal -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/build.xml b/build.xml index 83cdb71..02df324 100644 --- a/build.xml +++ b/build.xml @@ -119,6 +119,7 @@ + diff --git a/docs/case-mapping.md b/docs/case-mapping.md index 2ecf5fc..da6ece6 100644 --- a/docs/case-mapping.md +++ b/docs/case-mapping.md @@ -4,90 +4,100 @@ Case-Mapping Introduction ------------ -> Case mapping or case conversion is a process whereby strings are converted -> to a particular form—uppercase, lowercase, or titlecase—possibly for display -> to the user. +_Case-mapping_ or _case conversion_ is performed everytime a character is +changed from _upper case_ to _lower case_, or from _lower case_ to _upper case_. +_Case converter_ performs _case-mapping_ everytime you use it. -PHP always performed _Simple Case-Mapping_, this is map one-to-one character -mapping. For example, one _lower case_ character is converter to one _upper -case_ character. +There are two kind of case-mapping: -PHP 7.3 introduced [Full Case-Mapping], you can have one-to-many character -mapping. In practice this means than you can have different results depending on -your PHP version. - -```php -$german = new Convert('Straße'); - -echo $german->toUpper(); -// Produces STRAßE on PHP 7.2 -// Produces STRASSE on PHP 7.3 -``` +1. _Simple case-mapping_ +2. _Full case-mapping_ -Please note that _Full Case-Mapping_ is locale dependent: +_Simple case-mapping_ is one-to-one character mapping, for example a single +character "`A`" is replaced with another single character "`a`". -```php -// Turkish (requires appropriate locale) -$tur = new Convert('istambul'); -echo $tur->toTrain(); // output: İstanbul -``` +As you can image, _Full case-mapping_ performs one-to-many character +replacements (more precisely one-to-many code-points). +In real world use-cases, it's rare to perform _full case-mapping_, this is +because it only concerns a very small set of characters. For example in german +language, the letter "`ß`" is strictly lowercase and should be mapped to "`SS`" +in uppercase words. -Forcing _Simple Case-Mapping_ ------------------------------- +Case-Converter behaviour +------------------------ -As told before, _Full Case-Mapping_ is only available on PHP 7.3 and newer. - -The following code snippet is executed on PHP 7.3: +By default, Case-Converter will perform _full case-mapping_ ```php -// German +// Full case-mapping $ger = new Convert('Straße'); -echo $ger->toUpper(); // output: STRASSE +echo $ger->toUpper(); // output: STRASSE ``` -To force _Simple Case-Mapping_ you have to call `->forceSimpleCaseMapping()`: +If you want to perform _simple case-mapping_ then you have to +call `->forceSimpleCaseMapping()`: ```php -// German +// Simple case-mapping $ger = new Convert('Straße'); $ger->forceSimpleCaseMapping(); -echo $ger->toUpper(); // output: STRAßE +echo $ger->toUpper(); // output: STRAßE ``` -Please note `->forceSimpleCaseMapping()` has no effect on _PHP 7.1_ and _PHP -7.2_ as these version can only perform _Simple Case-Mapping_. - -Technical details ------------------ +As you can see, in _full case-mapping_ string length can change. -Internally `Case-Converter` uses [mb_convert_case()], this function uses the -following constants: +Case-Mapping in PHP +------------------- -- MB_CASE_LOWER -- MB_CASE_TITLE -- MB_CASE_UPPER - -The problem is that, Before _PHP 7.3_, these constants perform simple -case-mapping and after _PHP 7.3_ perform full case-mapping. +PHP 7.3 introduced _full case-mapping_, you can have one-to-many character +mapping. In practice this means than you can have different results depending on +your PHP version. -If you want to maintain the old functionality after _PHP 7.3_ you have to call -`->forceSimpleCaseMapping()`: +Internally Case-Converter uses _mb_convert_case()_ . This function works in +conjunction with specific constants to tell what action to perform. For example: ```php -// German -$ger = new Convert('Straße'); -$ger->forceSimpleCaseMapping(); -echo $ger->toUpper(); // output: STRASSE +mb_convert_case('Foo', MB_CASE_UPPER); // FOO ``` -*** +Prior to PHP 7.3, these were the available constants and their use: + +| Constant | Meaning | +|---------------|---------------------------------------------| +| MB_CASE_UPPER | Performs simple upper-case fold conversion. | +| MB_CASE_LOWER | Performs simple lower-case fold conversion. | +| MB_CASE_TITLE | Performs simple title-case fold conversion. | + +But from PHP 7.3, new constants were added and their meaning changed: + +| Constant | Meaning | +|----------------------|---------------------------------------------| +| MB_CASE_UPPER | Performs a full upper-case folding. | +| MB_CASE_LOWER | Performs a full lower-case folding. | +| MB_CASE_TITLE | Performs a full title-case conversion. | +| MB_CASE_UPPER_SIMPLE | Performs simple upper-case fold conversion. | +| MB_CASE_LOWER_SIMPLE | Performs simple lower-case fold conversion. | +| MB_CASE_TITLE_SIMPLE | Performs simple title-case fold conversion. | + +Locale dependent mapping +------------------------ + +Some case-mapping are locale dependent. This is the case of Turkish where the +small letter "`i`" should be replaced by a capital letter with a dot "`İ`". +However, according to documentation: -IMHO this is a _breaking change_, PHP people should have keep untouched old -constants and create new ones for [Full Case-Mapping], for example: -`MB_CASE_LOWER_FULL`, `MB_CASE_TITLE_FULL`, and `MB_CASE_UPPER_FULL` (please -note these constants do not exist). +> Only unconditional, language agnostic full case-mapping is performed. -[Full Case-Mapping]: https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.mbstring.case-mapping-folding +This means that locale dependent mapping are ignored and not performed. -[mb_convert_case()]: https://www.php.net/manual/en/function.mb-convert-case.php +Resources +--------- +
+
PHP 7.3 Full Case-Mapping and Case-Folding Support
+
https://www.php.net/manual/en/migration73.new-features.php#migration73.new-features.mbstring.case-mapping-folding
+
mb_convert_case()
+
https://www.php.net/manual/en/function.mb-convert-case.php
+
mbstring constant
+
https://www.php.net/manual/en/mbstring.constants.php
+