5d154fb304
This PR was merged into the 5.0-dev branch. Discussion ---------- [String] a new component for object-oriented strings management with an abstract unit system | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - This is a reboot of #22184 (thanks @hhamon for working on it) and a generalization of my previous work on the topic ([patchwork/utf8](https://github.com/tchwork/utf8)). Unlike existing libraries (including `patchwork/utf8`), this component provides a unified API for the 3 unit systems of strings: bytes, code points and grapheme clusters. The unified API is defined by the `AbstractString` class. It has 2 direct child classes: `BinaryString` and `AbstractUnicodeString`, itself extended by `Utf8String` and `GraphemeString`. All objects are immutable and provide clear edge-case semantics, using exceptions and/or (nullable) types! Two helper functions are provided to create such strings: ```php new GraphemeString('foo') == u('foo'); // when dealing with Unicode, prefer grapheme units new BinaryString('foo') == b('foo'); ``` `GraphemeString` is the most linguistic-friendly variant of them, which means it's the one ppl should use most of the time *when dealing with written text*. Future ideas: - improve tests - add more docblocks (only where they'd add value!) - consider adding more methods in the string API (`is*()?`, `*Encode()`?, etc.) - first class Emoji support - merge the Inflector component into this one - use `width()` to improve `truncate()` and `wordwrap()` - move method `slug()` to a dedicated locale-aware service class - propose your ideas (send PRs after merge) Out of (current) scope: - what [intl](https://php.net/intl) provides (collations, transliterations, confusables, segmentation, etc) Here is the unified API I'm proposing in this PR, borrowed from looking at many existing libraries, but also Java, Python, JavaScript and Go. ```php function __construct(string $string = ''); static function unwrap(array $values): array static function wrap(array $values): array function after($needle, bool $includeNeedle = false, int $offset = 0): self; function afterLast($needle, bool $includeNeedle = false, int $offset = 0): self; function append(string ...$suffix): self; function before($needle, bool $includeNeedle = false, int $offset = 0): self; function beforeLast($needle, bool $includeNeedle = false, int $offset = 0): self; function camel(): self; function chunk(int $length = 1): array; function collapseWhitespace(): self function endsWith($suffix): bool; function ensureEnd(string $suffix): self; function ensureStart(string $prefix): self; function equalsTo($string): bool; function folded(): self; function ignoreCase(): self; function indexOf($needle, int $offset = 0): ?int; function indexOfLast($needle, int $offset = 0): ?int; function isEmpty(): bool; function join(array $strings): self; function jsonSerialize(): string; function length(): int; function lower(): self; function match(string $pattern, int $flags = 0, int $offset = 0): array; function padBoth(int $length, string $padStr = ' '): self; function padEnd(int $length, string $padStr = ' '): self; function padStart(int $length, string $padStr = ' '): self; function prepend(string ...$prefix): self; function repeat(int $multiplier): self; function replace(string $from, string $to): self; function replaceMatches(string $fromPattern, $to): self; function slice(int $start = 0, int $length = null): self; function snake(): self; function splice(string $replacement, int $start = 0, int $length = null): self; function split(string $delimiter, int $limit = null, int $flags = null): array; function startsWith($prefix): bool; function title(bool $allWords = false): self; function toBinary(string $toEncoding = null): BinaryString; function toGrapheme(): GraphemeString; function toUtf8(): Utf8String; function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; function truncate(int $length, string $ellipsis = ''): self; function upper(): self; function width(bool $ignoreAnsiDecoration = true): int; function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): self; function __clone(); function __toString(): string; ``` `AbstractUnicodeString` adds these: ```php static function fromCodePoints(int ...$codes): self; function ascii(array $rules = []): self; function codePoint(int $index = 0): ?int; function folded(bool $compat = true): parent; function normalize(int $form = self::NFC): self; function slug(string $separator = '-'): self; ``` and `BinaryString`: ```php static function fromRandom(int $length = 16): self; function byteCode(int $index = 0): ?int; function isUtf8(): bool; function toUtf8(string $fromEncoding = null): Utf8String; function toGrapheme(string $fromEncoding = null): GraphemeString; ``` Case insensitive operations are done with the `ignoreCase()` method. e.g. `b('abc')->ignoreCase()->indexOf('B')` will return `1`. For reference, CLDR transliterations (used in the `ascii()` method) are defined here: https://github.com/unicode-org/cldr/tree/master/common/transforms Commits -------dd8745aced
[String] add more tests82a00956bc
[String] add tests012e92a772
[String] a new component for object-oriented strings management with an abstract unit system
168 lines
6.2 KiB
JSON
168 lines
6.2 KiB
JSON
{
|
|
"name": "symfony/symfony",
|
|
"type": "library",
|
|
"description": "The Symfony PHP framework",
|
|
"keywords": ["framework"],
|
|
"homepage": "https://symfony.com",
|
|
"license": "MIT",
|
|
"authors": [
|
|
{
|
|
"name": "Fabien Potencier",
|
|
"email": "fabien@symfony.com"
|
|
},
|
|
{
|
|
"name": "Symfony Community",
|
|
"homepage": "https://symfony.com/contributors"
|
|
}
|
|
],
|
|
"require": {
|
|
"php": "^7.2.9",
|
|
"ext-xml": "*",
|
|
"doctrine/event-manager": "~1.0",
|
|
"doctrine/persistence": "~1.0",
|
|
"twig/twig": "^2.10|^3.0",
|
|
"psr/cache": "~1.0",
|
|
"psr/container": "^1.0",
|
|
"psr/link": "^1.0",
|
|
"psr/log": "~1.0",
|
|
"symfony/contracts": "^1.1.7|^2",
|
|
"symfony/polyfill-ctype": "~1.8",
|
|
"symfony/polyfill-intl-grapheme": "~1.0",
|
|
"symfony/polyfill-intl-icu": "~1.0",
|
|
"symfony/polyfill-intl-idn": "^1.10",
|
|
"symfony/polyfill-intl-normalizer": "~1.0",
|
|
"symfony/polyfill-mbstring": "~1.0",
|
|
"symfony/polyfill-php73": "^1.11"
|
|
},
|
|
"replace": {
|
|
"symfony/asset": "self.version",
|
|
"symfony/amazon-mailer": "self.version",
|
|
"symfony/browser-kit": "self.version",
|
|
"symfony/cache": "self.version",
|
|
"symfony/config": "self.version",
|
|
"symfony/console": "self.version",
|
|
"symfony/css-selector": "self.version",
|
|
"symfony/dependency-injection": "self.version",
|
|
"symfony/debug-bundle": "self.version",
|
|
"symfony/doctrine-bridge": "self.version",
|
|
"symfony/dom-crawler": "self.version",
|
|
"symfony/dotenv": "self.version",
|
|
"symfony/error-handler": "self.version",
|
|
"symfony/error-renderer": "self.version",
|
|
"symfony/event-dispatcher": "self.version",
|
|
"symfony/expression-language": "self.version",
|
|
"symfony/filesystem": "self.version",
|
|
"symfony/finder": "self.version",
|
|
"symfony/form": "self.version",
|
|
"symfony/framework-bundle": "self.version",
|
|
"symfony/google-mailer": "self.version",
|
|
"symfony/http-client": "self.version",
|
|
"symfony/http-foundation": "self.version",
|
|
"symfony/http-kernel": "self.version",
|
|
"symfony/inflector": "self.version",
|
|
"symfony/intl": "self.version",
|
|
"symfony/ldap": "self.version",
|
|
"symfony/lock": "self.version",
|
|
"symfony/mailchimp-mailer": "self.version",
|
|
"symfony/mailer": "self.version",
|
|
"symfony/mailgun-mailer": "self.version",
|
|
"symfony/messenger": "self.version",
|
|
"symfony/mime": "self.version",
|
|
"symfony/monolog-bridge": "self.version",
|
|
"symfony/options-resolver": "self.version",
|
|
"symfony/postmark-mailer": "self.version",
|
|
"symfony/process": "self.version",
|
|
"symfony/property-access": "self.version",
|
|
"symfony/property-info": "self.version",
|
|
"symfony/proxy-manager-bridge": "self.version",
|
|
"symfony/routing": "self.version",
|
|
"symfony/security-core": "self.version",
|
|
"symfony/security-csrf": "self.version",
|
|
"symfony/security-guard": "self.version",
|
|
"symfony/security-http": "self.version",
|
|
"symfony/security-bundle": "self.version",
|
|
"symfony/sendgrid-mailer": "self.version",
|
|
"symfony/serializer": "self.version",
|
|
"symfony/stopwatch": "self.version",
|
|
"symfony/string": "self.version",
|
|
"symfony/templating": "self.version",
|
|
"symfony/translation": "self.version",
|
|
"symfony/twig-bridge": "self.version",
|
|
"symfony/twig-bundle": "self.version",
|
|
"symfony/validator": "self.version",
|
|
"symfony/var-dumper": "self.version",
|
|
"symfony/var-exporter": "self.version",
|
|
"symfony/web-link": "self.version",
|
|
"symfony/web-profiler-bundle": "self.version",
|
|
"symfony/workflow": "self.version",
|
|
"symfony/yaml": "self.version"
|
|
},
|
|
"require-dev": {
|
|
"cache/integration-tests": "dev-master",
|
|
"doctrine/annotations": "~1.0",
|
|
"doctrine/cache": "~1.6",
|
|
"doctrine/collections": "~1.0",
|
|
"doctrine/data-fixtures": "1.0.*",
|
|
"doctrine/dbal": "~2.4",
|
|
"doctrine/orm": "~2.4,>=2.4.5",
|
|
"doctrine/reflection": "~1.0",
|
|
"doctrine/doctrine-bundle": "^1.5|^2.0",
|
|
"masterminds/html5": "^2.6",
|
|
"monolog/monolog": "^1.25.1|^2",
|
|
"nyholm/psr7": "^1.0",
|
|
"ocramius/proxy-manager": "^2.1",
|
|
"php-http/httplug": "^1.0|^2.0",
|
|
"predis/predis": "~1.1",
|
|
"psr/http-client": "^1.0",
|
|
"psr/simple-cache": "^1.0",
|
|
"egulias/email-validator": "~1.2,>=1.2.8|~2.0",
|
|
"symfony/phpunit-bridge": "^3.4.31|^4.3.4|~5.0",
|
|
"symfony/security-acl": "~2.8|~3.0",
|
|
"phpdocumentor/reflection-docblock": "^3.0|^4.0",
|
|
"twig/cssinliner-extra": "^2.12",
|
|
"twig/inky-extra": "^2.12",
|
|
"twig/markdown-extra": "^2.12"
|
|
},
|
|
"conflict": {
|
|
"masterminds/html5": "<2.6",
|
|
"phpdocumentor/reflection-docblock": "<3.2.2",
|
|
"phpdocumentor/type-resolver": "<0.3.0",
|
|
"ocramius/proxy-manager": "<2.1",
|
|
"phpunit/phpunit": "<5.4.3"
|
|
},
|
|
"autoload": {
|
|
"psr-4": {
|
|
"Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/",
|
|
"Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/",
|
|
"Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/",
|
|
"Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/",
|
|
"Symfony\\Bundle\\": "src/Symfony/Bundle/",
|
|
"Symfony\\Component\\": "src/Symfony/Component/"
|
|
},
|
|
"classmap": [
|
|
"src/Symfony/Component/Intl/Resources/stubs"
|
|
],
|
|
"exclude-from-classmap": [
|
|
"**/Tests/"
|
|
]
|
|
},
|
|
"autoload-dev": {
|
|
"files": [
|
|
"src/Symfony/Component/String/Resources/functions.php",
|
|
"src/Symfony/Component/VarDumper/Resources/functions/dump.php"
|
|
]
|
|
},
|
|
"repositories": [
|
|
{
|
|
"type": "path",
|
|
"url": "src/Symfony/Contracts"
|
|
}
|
|
],
|
|
"minimum-stability": "dev",
|
|
"extra": {
|
|
"branch-alias": {
|
|
"dev-master": "5.0-dev"
|
|
}
|
|
}
|
|
}
|