This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/composer.json
Fabien Potencier 5d154fb304 feature #33553 [String] a new component for object-oriented strings management with an abstract unit system (nicolas-grekas, hhamon, gharlan)
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 tests
82a00956bc [String] add tests
012e92a772 [String] a new component for object-oriented strings management with an abstract unit system
2019-09-26 10:14:27 +02:00

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"
}
}
}