diff --git a/README.md b/README.md index 877075e..86cf064 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,20 @@ # Compass -> Библиотека работает с URL-адресами и с путями к файлам на диске. Compass написана на PHP и предлагает набор методов -> для обработки этих двух типов данных. Она может быть использована в различных приложениях, таких как веб-приложения -> для формирования и обработки путей к файлам или системы управления контентом для создания URL-адресов. +> Compass - это библиотека, предназначенная для работы с URL-адресами и путями к файлам на жестком диске. В ее состав +> входят инструменты для упрощения манипуляций с этими данными. Цель библиотеки - облегчить работу с URL-адресами и +> данными о расположении файлов. ## Общая информация -Эта библиотека предназначена для использования в более высокоуровневых PHP-проектах и включает в себя два основных -компонента: для работы с путями к файлам (`\Compass\Path`) и для работы с URL (`\Compass\Url`). Оба эти -компонента представлены отдельными объектами и предлагают набор методов, упрощающих манипуляции с путями и URL-адресами. +Compass может быть использован в различных PHP-приложениях для обработки путей к файлам и URL-адресов. Он включает в +себя два основных компонента: `Compass\Path` и `Compass\Url`. Оба этих компонента представляют собой отдельные объекты +и предлагают набор методов для простого манипулирования данными. `Compass\Path` предоставляет инструменты для работы с +директориями, включая поиск, замену и проверку наличия директорий в строке пути, а также изменение их порядка. В свою +очередь, `Compass\Url` обеспечивает возможности для работы с URL, позволяя создавать, получать и изменять различные +части URL-адреса. -`\Compass\Path` помогает упростить работу с директориями и файлами, позволяя находить и заменять определенные -директории по указанному шаблону, а также проверять наличие директорий в пути и изменять их порядок. Этот компонент -предоставляет возможность быстро и эффективно выполнять различные манипуляции с путями. - -В свою очередь, `\Compass\Url` позволяет работать с URL, предоставляя набор методов для создания, получения и -изменения различных частей URL-адреса. Особенностью этого компонента является то, что он создает URL оптимальным -образом, изменяя только те части адреса, которые требуется изменить, при создании множества URL одного домена. - -Схема показывает какие данные можно получить и модифицировать: - -``` - |---------------------------------------absolute url---------------------------------| - | | - |-----------------base url----------------|------------------relative url------------| - | | | - | authority | path query fragment| - | /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\|/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\ /‾‾‾‾‾‾‾‾\ /‾‾‾‾‾‾\| - |http://grigor:password@vinograd.soft:8080/path/to/resource.json?query=value#fragment| - \__/ \___/ \_____/ \___________/ \__/ \___/ - scheme user password host port suffix -``` - -Оба компонента работают по схожему принципу, сначала собирая необходимые параметры, а затем применяя их к -результату с помощью специального метода `updateSource()`. +Оба компонента работают по схожему принципу, сначала собирают необходимые параметры, а затем применяют их к результату с +помощью специального метода `updateSource()`. ## Установка @@ -53,109 +34,354 @@ php composer require vinogradsoft/compass "^1.0" ```php setScheme('https') - ->setUser('grigor') - ->setPassword('pass@word') - ->setHost('host.ru') - ->setPort('8088') - ->setPath('/user/index') - ->setSuffix('.php') - ->setArrayQuery([ - 'key1' => 'value1', - 'key2' => 'value2' - ])->setFragment('fragment'); +$url->setScheme('http')->setUser('grigor')->setPassword('password')->setHost('vinograd.soft') + ->setPort('8080')->setPath('/path/to/resource')->setSuffix('.json') + ->setArrayQuery(['query' => 'value',])->setFragment('fragment'); $url->updateSource(); -printUrl($url); +echo '

Authority: ', $url->getAuthority(); +echo '
Base Url: ', $url->getBaseUrl(); +echo '
Relative Url: ', $url->getRelativeUrl(); +echo '
Absolute Url: ', $url->getSource(), '
'; +echo '
Scheme: ', $url->getScheme(); +echo '
User: ', $url->getUser(); +echo '
Password: ', $url->getPassword(); +echo '
Host: ', $url->getHost(); +echo '
Port: ', $url->getPort(); +echo '
Path: ', $url->getPath(); +echo '
Suffix: ', $url->getSuffix(); +echo '
Query: ', $url->getQuery(); +echo '
Fragment: ', $url->getFragment(); + +$url->setSource('http://россия.рф'); +$url->setConversionIdnToAscii(true)->updateSource(); +echo '

new URL: ',$url; #http://xn--h1alffa9f.xn--p1ai +``` -$url = new Url('https://grigor:pass%40word@host.ru:8088/user/index?key1=value1&key2=value2#fragment'); -$url->setSuffix('.php'); -$url->updateSource(); +### Создание экземпляра класса Compass\Url + +Объект класса `Compass\Url` может быть создан путем вызова статического метода `createBlank()`, либо с помощью +оператора `new`. Метод `createBlank` примечателен тем, что в своей работе применяет клонирование своего же +прототипа. У данного метода есть два необязательных параметра: `$isIdnToAscii`, `$updateStrategy` - они определяют, +будет ли происходить преобразование хоста в `punycode`, и какую стратегию обновления URL применять. + +Первый параметр, `$isIdnToAscii` - определяет необходимость преобразования хоста. Если он принимает значение `true`, +хост преобразуется, если же `false` - преобразование хоста не происходит. + +Второй параметр, `$updateStrategy` - определяет стратегию обновления URL. Стратегия включает в себя методы создания +составляющих URL-адреса. + +Чтобы создать новый экземпляр класса `Compass\Url` с помощью оператора `new` необходимо передать один +обязательный параметр - это исходный URL в виде строки. + +### Варианты сборки URL + +Параметры можно устанавливать с помощью: + +- конструктора +- метода `$url->setSource(string $src);` +- метода `$url->setAll(array $parts);` +- методов отвечающих за конкретную часть Url (как показано в быстром старте). + +### Примеры + +#### **Через конструктор** + +```php +$url = new Url('http://grigor:password@vinograd.soft:8080/path/to/resource?query=value#fragment'); +``` + +#### **С помощью метода `$url->setSource(string $src);`** + +```php +$url->setSource('http://grigor:password@vinograd.soft:8080/path/to/resource?query=value#fragment'); +``` + +#### **С помощью метода `$url->setAll(array $parts);`** + +```php +$url->setAll([ + ':host' => 'host.ru', + ':scheme' => 'http', + ':user' => 'user', + ':password' => 'password', + ':port' => '80', + ':path' => ['path', 'to', 'resource'], + '?' => ['key' => 'value', 'key2' => 'value2'], + '#' => 'fragment', + ':suffix' => '.json', +]); +``` + +#### **С помощью методов отвечающих за конкретную часть Url.** + +```php +$url->setScheme('http')->setUser('grigor')->setPassword('password')->setHost('vinograd.soft') + ->setPort('8080')->setPath('/path/to/resource')->setSuffix('.json') + ->setArrayQuery(['query' => 'value',])->setFragment('fragment'); +``` + +В первых двух вариантах суффикс не распознается. В таких случаях суффикс необходимо установить отдельным методом +`$url->setSuffix(.json);`, только так вы сможете им управлять. + +> Суффикс не анализируется, так как он может быть любой строкой, и не обязательно начинаться с точки. Если вы передаете +> URL с суффиксом, то он становится частью path. -printUrl($url); +### Применение изменений + +Чтобы измененные параметры вступили в силу, необходимо вызвать метод `$url->updateSource()`. Этот метод имеет два +необязательных аргумента - `$updateAbsoluteUrl` и `$suffix`. Значение аргумента `$updateAbsoluteUrl` определяет, будет +ли обновляться весь URL-адрес или только его относительная часть. По умолчанию его значение равно `true`, другими +словами, по умолчанию будет пытаться обновить весь URL. Параметр `$suffix` имеет тип `string` и позволяет установить +суффикс в моменте обновления. + +После того как параметры были применены, вы можете получить обновленный результат, методом `$url->getSource()`. + +### Стратегии обновления + +> ***Стратегия обновления*** - это объект, который объединяет все входные данные для создания итогового URL-адреса. +> Этот объект должен быть реализацией интерфейса `Compass\UrlStrategy`. В системе, класс, который выполняет эту функцию, +> называется `Compass\DefaultUrlStrategy`.
+ +Стратегия придумана, для того, чтобы контролировать процесс создания URL. Она содержит набор методов по одному +на каждый участок URL-адреса, в которых происходит склеивание параметров. Какие методы будут участвовать в +обновлении определяется в объекте `Compass\Url` на основе состояний участков. Есть некие флаги которые говорят какие +участки нужно пересоздать. Для простоты восприятия можно провести аналогию с дорогой поделенной на какое-то количество +участков "A", "B", "C", "D" и дорожной компанией которая за нее ответственна. В идеале дорога должна быть всегда ровная. +Когда на дороге повреждается какой-нибудь участок, например "С", ремонтная бригада выезжает на этот участок и +ремонтирует его. Так же можно представить и URL-адрес, где дорога это строка у которой есть логические части - участки. +Ремонтные бригады - это методы в стратегии обновления. Дорожная компания - это объект `Compass\Url`. + +У стратегии есть шесть методов в которых создаются части URL-адреса: + +- `updateAuthority()` +- `updateBaseUrl()` +- `updateQuery()` +- `updatePath()` +- `updateRelativeUrl()` +- `updateAbsoluteUrl()`. + +Устанавливая какой-либо параметр для URL, система меняет состояние участка, как бы, повреждает тот +участок для которого был передан параметр. После вызова метода `$url->updateSource();` в работу включаются +соответствующие методы стратегии. + +> Важно запомнить, что в объекте `Compass\Url` хранятся исходные части, которые передал пользователь и результаты работы +> каждого метода стратегии. + +Выполнение методов можно поделить условно на три уровня. + +``` + УРОВЕНЬ 3 + |--------------------------------updateAbsoluteUrl()---------------------------------| + | | + | УРОВЕНЬ 2 | + |---------------updateBaseUrl()-----------|------------updateRelativeUrl()-----------| + | | | + | УРОВЕНЬ 1 | + | updateAuthority() | updatePath() updateQuery() | + | /‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\|/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\ /‾‾‾‾‾‾‾‾\ | + |http://grigor:password@vinograd.soft:8080/path/to/resource.json?query=value#fragment| +``` -function printUrl($url) +Метод третьего уровня `updateAbsoluteUrl()` выполняется всегда, в нем соединяются сохраненные результаты работы методов +второго уровня. Методы второго уровня `updateBaseUrl()` и `updateRelativeUrl()` соединяют результаты работы первого +уровня. На первом уровне `updateAuthority()`, `updatePath()` и `updateQuery()` склеивают исходные части каждый в своей +области. + +### Манипуляция состояниями + +Состояния хранятся в нескольких полях класса `Compass\Url`. `$authoritySate`, `$relativeUrlState` и `$schemeState`. +`$authoritySate` и `$relativeUrlState` имеют тип `int`, `$schemeState` - `bool`. Управление состояниями `$authoritySate` +и `$relativeUrlState` осуществляются битовыми операциями. Вот пример кода, который показывает их значения по умолчанию +когда состояние неповрежденное: + +```php +const USER_STATE = 1 << 0; +const PASSWORD_STATE = 1 << 1; +const HOST_STATE = 1 << 2; +const PORT_STATE = 1 << 3; + +const PATH_STATE = 1 << 0; +const QUERY_STATE = 1 << 1; +const FRAGMENT_STATE = 1 << 2; + +const AUTHORITY_WHOLE = self::USER_STATE | self::PASSWORD_STATE | self::HOST_STATE | self::PORT_STATE; +const RELATIVE_URL_WHOLE = self::PATH_STATE | self::QUERY_STATE | self::FRAGMENT_STATE; + +/** + * current states + */ +protected int $authoritySate = self::AUTHORITY_WHOLE; +protected int $relativeUrlState = self::RELATIVE_URL_WHOLE; +protected bool $schemeState = true; +``` + +Если мы хотим повредить состояние `$relativeUrlState` в области `query`, то мы можем воспользоваться побитовыми +операторами: + +```php +$relativeUrlState &= ~Url::QUERY_STATE; +``` + +Остальными участками можно манипулировать аналогичным способом, исключением является только `$schemeState` ему нужно +присвоить булево значение. + +### Пример создания своей стратегии + +При построении своего процесса обновления URL, иногда требуется, чтобы выполнился метод который на основе текущего +состояния не выполниться. В таких случаях используется дополнительный метод `forceUnlockMethod(...)` в котором можно +изменить текущее состояние определенного участка, тем самым заставить систему выполнить нужный метод. + +Это лучше понять на примере. Представим, что нам нужно генерировать URL для реферальных ссылок. Создадим стратегию в +которой будет добавляться для всех URL с доменом `another.site` параметр `refid` равный `40`. + +Код стратегии: + +```php +; + +use Compass\DefaultUrlStrategy; +use Compass\Url; + +class ReferralUrlStrategy extends DefaultUrlStrategy { - echo '

Authority: ', $url->getAuthority(); - echo '
BaseUrl: ', $url->getBaseUrl(); - echo '
RelativeUrl: ', $url->getRelativeUrl(); - echo '
AbsoluteUrl: ', $url; //$url->getSource(); - echo '
'; - - echo '
getScheme: ', $url->getScheme(); - echo '
getUser: ', $url->getUser(); - echo '
getPassword: ', $url->getPassword(); - echo '
getHost: ', $url->getHost(); - echo '
getPort: ', $url->getPort(); - echo '
getPath: ', $url->getPath(); - echo '
getSuffix: ', $url->getSuffix(); - echo '
getQuery: ', $url->getQuery(); - echo '
getFragment: ', $url->getFragment(); + + private bool $isAllowInsertParam = false; + + /** + * @inheritDoc + */ + public function updateQuery(array $items): string + { + if ($this->isAllowInsertParam) { + $items['refid'] = 40; + $this->isAllowInsertParam = false; + } + return http_build_query($items, '', '&', PHP_QUERY_RFC3986); + } + + /** + * @inheritDoc + */ + public function forceUnlockMethod( + bool &$schemeState, + int &$authoritySate, + int &$relativeUrlState, + array $items, + array $pathItems, + array $queryItems, + bool $updateAbsoluteUrl, + ?string $suffix = null + ): void + { + if ($items[Url::HOST] === 'another.site') { + $this->isAllowInsertParam = true; + } + $relativeUrlState &= ~Url::QUERY_STATE; + } + } ``` -Класс `\Compass\Url` может быть создан путем вызова статического метода `createBlank()` либо созданием нового -объекта оператором `new`. Метод `createBlank` примечателен тем, что в своей работе применяет клонирование своего же -прототипа. У данного метода есть два необязательных параметра: `$isIdnToAscii`, `$updateStrategy` - они определяют, -будет ли происходить преобразование хоста в `punycode`, и какую стратегию обновления/создания URL применять. +Теперь установим стратегию и выведем два URL, один из которых будет целевым, с доменом `another.site`: -Первый параметр, `$isIdnToAscii` - определяет необходимость преобразования хоста при создании URL. Если он принимает -значение `true`, хост преобразуется, если же `false` - преобразование хоста не происходит. Этот параметр можно изменить -после создания экземпляра класса через метод `setConversionIdnToAscii()` - ему нужно передать новое значение. +```php +$url = Url::createBlank(); +$url->setUpdateStrategy(new ReferralUrlStrategy()); -Второй параметр, `$updateStrategy` - определяет стратегию обновления URL или его создания. Он включает в себя выбор -метода создания URL и его составляющих. +$url->setSource('https://another.site'); +$url->updateSource(); +echo $url->getSource(), '
'; # https://another.site/?refid=40 -Чтобы создать новый экземпляр класса `\Compass\Url` с помощью оператора `new` необходимо передать один -обязательный параметр - это исходный URL в виде строки. +$url->setHost('vinograd.soft'); +$url->updateSource(); +echo $url->getSource(); # https://vinograd.soft +``` -После настройки всех параметров для внесения изменений необходимо вызвать метод `updateSource();`. +На первый взгляд может показаться, что изменение состояния (`$relativeUrlState &= ~Url::QUERY_STATE;`) нужно было +написать внутри `if` конструкции, но это не так. После того как мы установили хост `vinograd.soft`, вызов +метода `updateSource()` не привел бы к выполнению метода `updateQuery` нашей стратегии, поскольку не было добавлено ни +одного параметра штатным способом, который мог бы изменить состояние этого участка. В результате состояние осталось бы +не поврежденным, обновился бы только участок `baseurl` и сохраненный параметр `refid=40` с прошлого раза, был бы склеен +с новым `baseurl` который содержит хост `vinograd.soft`. +Этот пример показывает, что вы не ограничены только штатными методами установки частей URL. Используя стратегии можно +делать пост обработку результата, например когда нужно экранировать результат для HTML-атрибутов содержащих URL (href, +src и другие атрибуты этого типа). --- ## Компонент PATH -### Быстрый старт +`Compass\Path` можно охарактеризовать как объектное представление пути к файлу. Он оперирует строкой пути не +опираясь на реальную файловую систему. Компонент так же как и `Compass\Url` имеет стратегию обновления, которая включает +в себя один метод `updatePath()`. Важно отметить, что этот компонент не имеет состояний. -```php +### Демонстрация методов + +```php replaceAll([ '__NAME__' => 'User', '__NAME2__' => 'Filesystem', ]); $path->updateSource(); -echo '
', $path; -$path->setAll(['path','to','file.txt']); +echo '
', $path; # /User/UserScanner/FilesystemDriver + +$path->setAll(['path', 'to', 'file.txt']); $path->updateSource(); -echo '
', $path; -``` -Результат вывода: +echo '
', $path; # path/to/file.txt -``` -/User/UserScanner/FilesystemDriver -path/to/file.txt -``` +$path->set(1, 'newTo'); +$path->updateSource(); ---- +echo '
', $path; # path/newTo/file.txt + +$path->setBy('path', 'newPath'); +$path->updateSource(); + +echo '
', $path; # newPath/newTo/file.txt + +echo '
', $path->dirname(); # newPath/newTo + +$path->setSuffix('.v'); +$path->updateSource(); +echo '
', $path; # newPath/newTo/file.txt.v + +$path->setSource('newPath/newTo/file'); +$path->setSuffix('.v'); +$path->updateSource(); +echo '
', $path; # newPath/newTo/file.v + +$path->replace('newPath','path'); +$path->updateSource(); +echo '
', $path; # path/newTo/file.v +``` ## Тестировать ``` php composer tests ``` + + +### Содействие +Пожалуйста, смотрите [ВКЛАД](https://github.com/vinogradsoft/compass/blob/master/CONTRIBUTING.md) для получения подробной информации. + +### Лицензия +Лицензия MIT (MIT). Пожалуйста, смотрите [файл лицензии](https://github.com/vinogradsoft/compass/blob/master/LICENSE) для получения дополнительной информации. \ No newline at end of file