У меня есть проект, который представляет собой полностью функциональный код, например: все функции статичны и не должны зависеть от того, как они были вызваны. Если вы вызываете функцию foo("hello", 12.345, true);
, контекст вызова этой функции не должен иметь значения — она всегда должна выполнять одно и то же действие.
У меня проблема в том, что я использую две сторонние библиотеки, которые благодаря хладнокровию импортируют GuzzleHttp и определяют его как пространство имен в своих автозагрузчиках, хотя используют разные версии клиента Guzzle. (Два сторонних SDK, которые я импортировал, предназначены для Plivo и libphonenumber, хотя не уверен, что это актуально.) То, как написаны функции, я делаю примерно так:
function sendPlivoSms($number, $message) {
require_once("/vendor/plivo/autoloader.php");
$gz_client = new \GuzzleHttp\Client();
// more code here
}
function formatNationalPhoneNumber($number) {
require_once("/vendor/libphonenumber/autoloader.php");
$gz_client = new \GuzzleHttp\Client();
// more code here
}
Проблема возникает, когда я вызываю formatNationalPhoneNumber
перед sendPlivoSms
- он загружает пространство имен GuzzleHttp, что затем приводит к тому, что функция отправки SMS вызывает неправильный класс клиента и, следовательно, прерывается (в частности, из-за отсутствующей функции chooseHandler
). Автозагрузчики загружают разные версии кода Guzzle в разных местах исходного кода (что на 100% нормально, за исключением перехвата пространства имен).
Есть ли способ изолировать эти вызовы функций, чтобы не украсть ссылки на пространство имен друг друга?
Например, могу ли я «сбросить» пространство имен в конце вызова функции (опять же: чтобы функция оставалась совершенно статической)? Или есть способ как-то поместить пространство имен в ограниченную область?
Дерево файлов и composer.json следующие:
/
composer.json
tons of other directories and code here...
./thirdparty/
./libphonenumber/
./vendor/
./composer.json
./vendor/
all composer directories loaded here, except the libphonenumber ones
Я, должно быть, неправильно настроил их, когда устанавливал их изначально. Я новичок в Composer, и, хотя я понимаю пространства имен, тот факт, что они требуют одной и той же библиотеки (GuzzleHttp), но разных версий, мне показалось, что это проблема управления библиотекой, которую я даже не предвидел. чтобы справиться с этим (будет ли он динамически переименовывать пространства имен? если они должны быть уникальными, но конфликтуют, как еще он может их разрешить?). Вот почему я спросил об изоляции, в конце концов, если я запускаю эти вызовы функций как независимые потоки, они ДЕЙСТВИТЕЛЬНО работают так, как предполагалось.
Этот комментарий определенно звучит так, будто я облажался, пытаясь управлять версиями пакетов с одной установкой Composure, если только я что-то не понимаю: И НЕТ, вы не можете устанавливать разные версии одного и того же пакета композитором. Я поражен, если нет ответа на мой основной вопрос: есть ли способ изолировать эти вызовы функций, чтобы не воровать ссылки на пространства имен друг друга?
Они не «крадут ссылки друг у друга на пространство имен». Существует одно пространство имен с заданным именем. Какая бы ветвь кода ни называла это имя первой, она и будет «выиграть» ссылки на нее в будущем. У вас не может быть нескольких версий одного и того же пространства имен в одном скрипте. Если вы используете один экземпляр композитора для управления обоими пакетами, он, скорее всего, сможет найти и установить версию Guzzle, которая работает с обеими библиотеками. Тем не менее, giggsey/libphonenumber-for-php, на который вы ссылались, даже не использует Guzzle, поэтому я не уверен, как вы видите конфликт. Разместите свой composer.json
файл.
Извините, слово «воровать» может быть неправильным, поэтому я использовал слово «конфликт» в заголовке своего вопроса. Как я уже сказал, я заметил, что первый заявитель кажется «выигравшим», что вызвало проблему, когда это был «неправильный» пакет. Я обновил его, включив в него часть моей фактической файловой структуры и соответствующие файлы composer.json в соответствии с вашими отзывами (которые я очень ценю)! (И я понимаю из этого разговора, что у меня, вероятно, должен быть только 1 в корне.)
То, что написал Алекс, и для меня, если вы позволите комментарий, выглядит так, как будто вам нужно было обмануть композитора, в то время как для того, что вы описываете, вы могли бы позволить композитору сделать тяжелую работу по управлению зависимостями (что на самом деле то, что для этого написано). В том неудачном случае, если у вас действительно есть конфликт зависимостей, который блокирует разрешение всего набора, вам обязательно нужно решить его по-другому. thirdparty
и т. д. и более заметный с require_once("/vendor/libphonenumber/autoloader.php")
выглядит громоздко для меня. строить обратно.
И нет, код не является чисто функциональным. Это потому, что он содержит оператор require
, который имеет побочные эффекты. Кроме того, он зависит от состояния файловой системы, которая подвержена гонкам.
@hakre Чтобы не отходить от темы, но какой код не подлежит условиям гонки? Если у меня есть фундаментальное непонимание «функционального кода», я хотел бы заняться самообразованием, но в настоящее время я не понимаю, как условия гонки играют роль в этом определении.
@Bing: Да, оставьте в стороне условия гонки, побочные эффекты - вот что здесь болит: как только класс загружен, второй автозагрузчик не сработает, и его все равно нельзя заменить. У вас есть две функции, но имеет смысл вызывать только одну из них для каждого процесса PHP. Технически у вас есть две программы, и с таким мышлением это естественно работает.
У вас здесь множество проблем.
Ваша основная проблема заключается в том, что у вас не должно быть thirdparty
в качестве подкаталога. Этот код для libphonenumber должен находиться в подкаталоге верхнего уровня vendor
вместе с другими библиотеками. Вы не должны загружать, устанавливать или поддерживать его самостоятельно, потому что (как вы заметили) вы создадите конфликты. Пусть Composer сделает всю работу.
Удалите подкаталог thirdparty
и запустите composer require giggsey/libphonenumber-for-php
в каталоге проекта верхнего уровня. Это загрузит libphonenumber в подкаталог vendor
и добавит его имя в файл composer.json
вашего проекта.
Затем в своем собственном исходном коде вы хотите использовать только один автозагрузчик верхнего уровня, созданный для вас Composer:
require_once 'vendor/autoload.php';
(Или каким бы ни был путь относительно вашего кода.)
Это должно быть запущено один раз, при инициализации. Не включайте в каждую функцию, которая использует внешнюю библиотеку. Учитывая это, вам не нужно беспокоиться о разных версиях Guzzle. Какая бы версия не была установлена Composer, она будет работать для всего, что в ней нуждается.
Тем не менее, похоже, что вы используете необычайно старые версии вещей. Вы находитесь в:
"monolog/monolog": "1.0.*",
Этой версии больше десяти лет! У вас также есть:
"ext-mcrypt": "*",
Это указывает на то, что вы, вероятно, используете PHP 5, срок службы которого истек несколько лет назад. Если ваш код такой старый, то вы не можете ожидать, что текущие версии современных библиотек, таких как Guzzle, будут хорошо с ним работать.
Что бы это ни стоило, многие из них даже не используются, а просто автоматически включаются моей IDE. Я потратил каждый рабочий час с момента этого комментария на его реализацию и добился большого прогресса. Этому проекту почти 10 лет, поэтому некоторые из этих самоуправляемых решений (плюс страх перед npm left-pad). Спасибо за ваш вклад, я искренне принял это близко к сердцу и уже реализовал 80% из них.
«контекст вокруг этого вызова функции не должен иметь никакого значения - он всегда должен выполнять одно и то же действие». Похоже, у вас есть фундаментальное непонимание того, как работают пространства имен. Пространства имен уникальны. Вы не можете ожидать, что два разных вызова функции с одним и тем же именем запустят два разных фрагмента кода. Я не понимаю, почему у вас есть автозагрузчик для каждой библиотеки, когда обе библиотеки контролируются Composer. На весь проект должен быть один автозагрузчик.