Это статья, переведенная сообществом, и перевод был завершен. Для получения дополнительной информации, пожалуйста, нажмитеВведение в совместный перевод.
У меня есть для вас сложная задача. Далее вы будетениктоС рамочного подхода начинается путешествие проекта. Во-первых, это не вонючая и длинная антирамная стелька для ног. И это не продвижениенеоригинальныйМысль . В конце концов, мы также будем использовать вспомогательные пакеты, написанные другими разработчиками фреймворка, до конца нашего пути разработки. Я также безукоризненно отношусь к инновациям в этой области.
Дело не в других, дело в себе. Это даст вам возможность расти как разработчику.
Возможно, одна из преимуществ бескаркасной разработки заключается в том, что вы можете получить обширные знания из лежащих в основе процессов. Откажитесь от волшебных фреймворков, которые помогают вам справляться с вещами, которые вы не можете отладить и не можете по-настоящему понять, и вы увидите, как именно все это происходит.
Скорее всего, на следующей работе вы не сможете выбирать фреймворк для разработки новых проектов по своему желанию. Реальность такова, что многие важные для бизнеса задачи PHP используют существующие приложения. И независимо от того, построено ли приложение в современных популярных фреймворках, таких как Laravel или Symfony, или в старом и устаревшем CodeIgniter или FuelPHP, или оно может широко использоваться в разочаровывающих«Включить архитектурно-ориентированные» традиционные PHP-приложениясреди, такниктоРазработка фреймворка будет тем, с чем вы столкнетесь в будущемлюбойПомочь вам в вашем проекте PHP.древние времена, потому чтоопределенные системыНеобходимость объяснять распределение HTTP-запросов, отправку HTTP-ответов, управление зависимостями и разработку без фреймворка — это болезненная битва. Отсутствие отраслевых стандартов обязательно означает, что эти компоненты в структуре сильно связаны. Если вы начнете без фреймворка, в конечном итоге вы создадите свой собственный фреймворк.
Сегодня, благодаряPHP-FIGСо всей автозагрузкой и интерактивностью безрамочная разработка не начинается с нуля. Есть так много отличных интерактивных пакетов от разных поставщиков. Сочетать их проще, чем вы думаете!
Переведено wilson_yang 1 день назад 4 ретранслировать Зависит отSummerобзорКак работает PHP?
Прежде чем делать что-либо еще, PHP очень важно выяснить, как общаться с внешним миром.
PHP запускает серверные приложения в цикле запрос/ответ. Каждое взаимодействие с вашим приложением — будь то из браузера, командной строки или REST API — поступает в приложение как запрос. После получения запроса:
- программа запускается;
- начать обработку запроса;
- произвести ответ;
- Затем ответ возвращается соответствующему клиенту, сгенерировавшему запрос;
- Наконец программа закрывается.
передний контроллер
Вооружившись этими знаниями, пора приступить к программированию нашего фронт-контроллера. Фронт-контроллер — это файл PHP, который обрабатывает каждый запрос от программы. Контроллер — это первый файл PHP, который встречается при поступлении запроса в вашу программу, и (по сути) последний файл, через который проходит ответ из вашего приложения.
Мы используем классическийHello, world!В качестве примера, чтобы убедиться, что все подключено правильно, этот пример приведенВстроенный сервер PHPводить машину. Прежде чем начать это делать, убедитесь, что вы установили PHP7.1 или выше. Создайте содержащийpublic
элементы каталога, а затем создайтеindex.php
файл, напишите в файле следующий код:<?php
declare(strict_types=1);
echo 'Hello, world!';
Обратите внимание, что здесь мы объявляем использование строгого режима — рекомендуетсяначало каждого файла PHPоба делают. Потому что для разработчиков, идущих сзади, вы вводите подсказкиОтладка и четкое информирование о намерениях важны.Используйте командную строку (например, терминал macOS), чтобы перейти в каталог вашего проекта и запустить встроенный сервер PHP.
php -S localhost:8080 -t public/
Теперь откройте в браузереhttp://localhost:8080/. Вы успешно увидели вывод «Hello, world!»?
очень хороший. Теперь мы можем перейти к делу!
Переведено JiaZombie 1 день назад 0 ретранслировать Зависит отSummerобзорАвтозагрузка со сторонними пакетами
Когда вы используете PHP в первый раз, вы можете использовать в своей программеincludes
или requires
оператор для импорта функциональности и конфигурации из других файлов PHP. Обычно мы избегаем этого, потому что другим будет сложнее следить за вашим кодом и понимать, где находятся зависимости. Это делает отладкудействительнокошмарный сон. Решение - использовать автозагрузку. Автозагрузка означает: когда вашей программе нужно использовать класс,
PHP знает, где найти и загрузить этот класс при его вызове. Хотя эта функция доступна начиная с PHP 5, благодаряPSR-0(Стандарт автозагрузки, позжеPSR-4заменены), его использование только начало расти. Мы могли бы написать свой собственный автозагрузчик, чтобы выполнить эту работу, но поскольку мы будем использовать тот, который управляет зависимостями сторонних производителей
ComposerВполне пригодный для использования автозагрузчик уже включен, так что давайте просто воспользуемся им. Убедитесь, что вы уже находитесь в своей системеУстановитьКомпозитор. Затем инициализируйте Composer для этого проекта:composer init
Эта команда интерактивно проведет вас через созданиеcomposer.json
конфигурационный файл. Как только файл создан, мы можем открыть его в редакторе и записать в негоautoload
поле, чтобы оно выглядело так (это гарантирует, что автозагрузчик знает, где найти классы в нашем проекте):{
"name": "kevinsmith/no-framework",
"description": "An example of a modern PHP application bootstrapped without a framework.",
"type": "project",
"require": {},
"autoload": {
"psr-4": {
"ExampleApp\\": "src/"
}
}
}
Теперь установите для этого проекта composer, который подтянет зависимости (если они есть) и создаст для нас автозагрузчик:
composer install
возобновитьpublic/index.php
файл для представления автозагрузчика. В идеале это должен быть один из немногих операторов включения, которые вы используете в своей программе.<?php
declare(strict_types=1);
require_once dirname(__DIR__) . '/vendor/autoload.php';
echo 'Hello, world!';
Если вы обновите свой браузер на этом этапе, вы не увидите никаких изменений. Поскольку автозагрузчик не модифицировал и не выдавал никаких данных, мы видим то же самое. давайте положимHello, world!Переместите этот пример в уже загруженный автоматически класс, чтобы увидеть, как он работает.
В корневом каталоге проекта создайте файл с именемsrc
каталог, а затем добавьте файл с именемHelloWorld.php
файл, напишите следующий код:
<?php
declare(strict_types=1);
namespace ExampleApp;
class HelloWorld
{
public function announce(): void
{
echo 'Hello, autoloaded world!';
}
}
теперь кpublic/index.php
используется внутриHelloWorld
Категорияannounce
метод заменыecho
утверждение.
// ...
require_once dirname(__DIR__) . '/vendor/autoload.php';
$helloWorld = new \ExampleApp\HelloWorld();
$helloWorld->announce();
Обновите браузер, чтобы увидеть новую информацию!
Переведено JiaZombie 1 день назад 0 ретранслировать Зависит отSummerобзорЧто такое внедрение зависимостей?
Внедрение зависимостей — это метод программирования, при котором каждая зависимость предоставляет необходимый ей объект, а не получает требуемую информацию или функциональные возможности вне объекта.
Например, предположим, что метод класса в приложении должен читать из базы данных. Для этого вам нужно подключение к базе данных. Обычный метод заключается в создании нового подключения, которое будет видно глобально.
class AwesomeClass
{
public function doSomethingAwesome()
{
$dbConnection = return new \PDO(
"{$_ENV['type']}:host={$_ENV['host']};dbname={$_ENV['name']}",
$_ENV['user'],
$_ENV['pass']
);
// Make magic happen with $dbConnection
}
}
Но делать это грязно, это возлагает на нас ответственность, которой здесь нет - созданиеобъект подключения к базе данных,Проверить учетные данные,иОбрабатывать некоторые сбои подключения - это может вызвать проблемы в приложениимногоДублирующийся код. Если вы попытаетесь протестировать этот класс, он вообще не сработает. Этот класс тесно связан со средой приложения и базой данных.Вместо этого, почему бы не выяснить, что нужно вашему классу в первую очередь? Нам просто нужно сначала внедрить объект «PDO» в класс.
class AwesomeClass
{
private $dbConnection;
public function __construct(\PDO $dbConnection)
{
$this->dbConnection = $dbConnection;
}
public function doSomethingAwesome()
{
// Make magic happen with $this->dbConnection
}
}
Это более лаконично, ясно и легко для понимания и менее подвержено ошибкам. Благодаря подсказкам типов и внедрению зависимостей метод может четко и точно объявить, что он делает, не полагаясь на внешние вызовы для его получения. При выполнении модульных тестов мы можем хорошо смоделировать соединение с базой данных и передать его для использования.
внедрение зависимостиконтейнер— это инструмент, с помощью которого вы можете создавать и внедрять эти зависимости во все ваше приложение. Контейнеры не обязательно должны иметь возможность использовать методы внедрения зависимостей, но по мере роста и усложнения приложения это принесет большую пользу.
Мы будем использовать один из самых популярных DI-контейнеров в PHP: настоящийPHP-DI.(рекомендуется, чтобыЕще одно решение для внедрения зависимостейможет быть полезно читателям)
Переведено wilson_yang 1 день назад 0 ретранслировать Зависит отSummerобзорконтейнер для внедрения зависимостей
Теперь, когда у нас установлен Composer, установить PHP-DI очень просто, давайте вернемся к командной строке, чтобы сделать это.
composer require php-di/php-di
Исправлять public/index.php
Используется для настройки и сборки контейнеров.// ...
require_once dirname(__DIR__) . '/vendor/autoload.php';
$containerBuilder = new \DI\ContainerBuilder();
$containerBuilder->useAutowiring(false);
$containerBuilder->useAnnotations(false);
$containerBuilder->addDefinitions([
\ExampleApp\HelloWorld::class => \DI\create(\ExampleApp\HelloWorld::class)
]);
$container = $containerBuilder->build();
$helloWorld = $container->get(\ExampleApp\HelloWorld::class);
$helloWorld->announce();
Ничего страшного. Это по-прежнему простой пример с одним файлом, и вы можете легко увидеть, как он работает.
Пока что мы толькоНастроить контейнер, значит, мы должныЯвно объявить зависимости(Вместо того, чтобы использоватьавтоматическая сборка или аннотация) и извлекается из контейнераHelloWorld
объект.Совет: Автосвязывание — это полезная функция, когда вы начинаете создавать приложение, но она скрывает зависимости и ее сложно поддерживать. Вполне возможно, что в ближайшие несколько лет другой разработчик по незнанию представит новую библиотеку, а затем создаст ситуацию, когда несколько библиотек реализуют один интерфейс, что нарушит автосвязывание и приведет к тому, что берущие могут легко игнорировать невидимые проблемы.
насколько это возможноимпортировать пространство имен, что может повысить читаемость кода.
<?php
declare(strict_types=1);
use DI\ContainerBuilder;
use ExampleApp\HelloWorld;
use function DI\create;
require_once dirname(__DIR__) . '/vendor/autoload.php';
$containerBuilder = new ContainerBuilder();
$containerBuilder->useAutowiring(false);
$containerBuilder->useAnnotations(false);
$containerBuilder->addDefinitions([
HelloWorld::class => create(HelloWorld::class)
]);
$container = $containerBuilder->build();
$helloWorld = $container->get(HelloWorld::class);
$helloWorld->announce();
Сейчас кажется, что мы поднимаем шум из-за того, что делали раньше.
Не волнуйтесь, контейнеры пригодятся, когда мы добавим другие инструменты, помогающие нам направлять запросы. Он загружает нужные классы по требованию в нужное время.
Переведено wilson_yang 1 день назад 0 ретранслировать Зависит отSummerобзорпромежуточное ПО
Если вы думаете о своем приложении как о луковице, запрос приходит извне, достигает центра луковицы и, наконец, возвращается в виде ответа. Затем промежуточное ПО — это каждый слой луковицы. Он получает запросы и может их обрабатывать. Либо передайте запрос внутреннему уровню, либо верните ответ внешнему уровню (это может произойти, если ПО промежуточного слоя проверяет определенное условие, которому запрос не соответствует, например, запрос несуществующего маршрута).
Если запрос проходит через все слои, программа начинает его обрабатывать и преобразовывает в ответ, мидлвар получает ответ в порядке, обратном запросу, а также может модифицировать ответ перед передачей его следующему мидлвару.
Вот несколько основных примеров использования промежуточного программного обеспечения:
- Проблемы отладки в среде разработки
- Изящно обрабатывать исключения в рабочей среде
- Ограничение частоты входящих запросов
- Ответ на неподдерживаемый тип ресурса, переданный запросом
- Обработка совместного использования ресурсов между источниками (CORS)
- Направить запрос в правильный класс обработчика
Так является ли промежуточное ПО единственным способом реализовать эти функции? конечно, нет. Но реализация промежуточного программного обеспечения делает ваше понимание жизненного цикла запроса/ответа более ясным. Это также означает более легкую отладку и более быструю разработку для вас.
Мы выиграем от последнего варианта использования, перечисленного выше, который является маршрутизацией.
Переведено JiaZombie 1 день назад 0 ретранслировать Зависит отSummerобзормаршрутизация
Маршрутизация зависит от информации о входящем запросе, чтобы определить, какой класс должен ее обрабатывать. (например, URI/products/purple-dress/medium
должно бытьProductDetails::class
класс получает обработку, аpurple-dress
и medium
передается в качестве параметра) В примере приложения мы будем использовать популярныйFastRouteмаршрутизация, основанная наРеализация промежуточного программного обеспечения, совместимого с PSR-15.Планировщик ПО промежуточного слоя
Чтобы наше приложение могло работать с промежуточным программным обеспечением FastRoute и другим промежуточным программным обеспечением, которое мы установили, нам нужен планировщик промежуточного программного обеспечения.
PSR-15является стандартом промежуточного программного обеспечения (также называемым в спецификации «обработчиком запросов»), который определяет интерфейс для промежуточного программного обеспечения и планировщиков, что позволяет различным промежуточному программному обеспечению и планировщикам взаимодействовать друг с другом. Мы просто выбираем планировщик, совместимый с PSR-15, который гарантирует, что он будет работать с любым промежуточным программным обеспечением, совместимым с PSR-15. Давайте установимRelayкак планировщик.composer require relay/relay:2.x@dev
И в соответствии со стандартными требованиями промежуточного программного обеспечения PSR-15 для достижения переносимостиHTTP-сообщения, совместимые с PSR-7, Мы используем Zend DiactorosКак реализация ПСР-7.composer require zendframework/zend-diactoros
Мы используем Relay для получения промежуточного программного обеспечения.
// ...
use DI\ContainerBuilder;
use ExampleApp\HelloWorld;
use Relay\Relay;
use Zend\Diactoros\ServerRequestFactory;
use function DI\create;
// ...
$container = $containerBuilder->build();
$middlewareQueue = [];
$requestHandler = new Relay($middlewareQueue);
$requestHandler->handle(ServerRequestFactory::fromGlobals());
Мы используем в строке 16ServerRequestFactory::fromGlobals()
Пучок Необходимая информация для создания нового запроса объединяетсяЗатем передайте его Реле. это точноRequest
Введите начало нашего стека промежуточного программного обеспечения.Теперь давайте перейдем к добавлению FastRoute и промежуточного ПО обработчика запросов. (FastRoute определяет легитимность запроса, возможность его обработки приложением, после чего обработчик запросов отправляетRequest
к соответствующему обработчику, зарегистрированному в таблице конфигурации маршрутизации)
composer require middlewares/fast-route middlewares/request-handler
Затем мы даемHello, world!Класс обработчика определяет маршрут. Здесь мы используем/hello
Маршруты для предоставления маршрутов за пределами базового URI.
// ...
use DI\ContainerBuilder;
use ExampleApp\HelloWorld;
use FastRoute\RouteCollector;
use Middlewares\FastRoute;
use Middlewares\RequestHandler;
use Relay\Relay;
use Zend\Diactoros\ServerRequestFactory;
use function DI\create;
use function FastRoute\simpleDispatcher;
// ...
$container = $containerBuilder->build();
$routes = simpleDispatcher(function (RouteCollector $r) {
$r->get('/hello', HelloWorld::class);
});
$middlewareQueue[] = new FastRoute($routes);
$middlewareQueue[] = new RequestHandler();
$requestHandler = new Relay($middlewareQueue);
$requestHandler->handle(ServerRequestFactory::fromGlobals());
Для работы вам также необходимо изменитьHelloWorld
сделать его вызываемым классом, то естьЭтот класс можно вызывать произвольно, как функцию.
// ...
class HelloWorld
{
public function __invoke(): void
{
echo 'Hello, autoloaded world!';
exit;
}
}
(обратите внимание, что в магическом методе__invoke()
Присоединяйсяexit;
. Мы можем сделать это за 1 секунду - только не хочу, чтобы вы это пропустили)
Открой сейчасhttp://localhost:8080/hello, открой шампанское!
Переведено wilson_yang 18 часов назад 0 ретранслировать Зависит отSummerобзоруниверсальный клей
Проницательные читатели могут быстро заметить, что, хотя мы все еще находимся в ловушке конфигурирования и создания контейнеров внедрения зависимостей, контейнеры сейчас практически бесполезны для нас. Планировщики и промежуточное ПО работают и без него.
Так когда же он покажет свою силу?
Что ж, если -- в реальных приложениях всегда так --HelloWorld
Есть ли у класса зависимости?Давайте рассмотрим простую зависимость и посмотрим, что происходит.
// ...
class HelloWorld
{
private $foo;
public function __construct(string $foo)
{
$this->foo = $foo;
}
public function __invoke(): void
{
echo "Hello, {$this->foo} world!";
exit;
}
}
Обновите браузер..
ВАУ!посмотри на этоArgumentCountError
, Это происходит потому, чтоHelloWorld
Класс должен ввести строку, когда он сконструирован для запуска, до тех пор он может только ждать. ЭтотЭто болевая точка, которую контейнеры помогут вам решить.Мы определяем эту зависимость в контейнере, а затем передаем контейнер в
RequestHandler
идти с решить эту проблему.// ...
use Zend\Diactoros\ServerRequestFactory;
use function DI\create;
use function DI\get;
use function FastRoute\simpleDispatcher;
// ...
$containerBuilder->addDefinitions([
HelloWorld::class => create(HelloWorld::class)
->constructor(get('Foo')),
'Foo' => 'bar'
]);
$container = $containerBuilder->build();
// ...
$middlewareQueue[] = new FastRoute($routes);
$middlewareQueue[] = new RequestHandler($container);
$requestHandler = new Relay($middlewareQueue);
$requestHandler->handle(ServerRequestFactory::fromGlobals());
Ой! При обновлении браузера появится надпись «Привет, мир баров!»!
Переведено wilson_yang 1 день назад 2 ретранслировать Зависит отSummerобзоротправить ответ правильно
Вы помните, что я упоминал ранее вHelloWorld
в классеexit
приговор? Когда мы создаем код, это позволяет нам получить простой и грубый ответ, но это ни в коем случае не лучший выбор для вывода в браузер. Эта грубая практика даетHelloWorld
При дополнительной работе с ответами, которую на самом деле должны выполнять другие классы, было бы слишком сложно отправить все заголовки икод состояния, а затем сразу же закрыть приложение, сделавHelloWorld
послеУ промежуточного программного обеспечения также нет шансов запуститься. Помните, каждое промежуточное ПО имеет возможностьRequest
Измените его при входе в наше приложение, а затем (в обратном порядке) измените ответ при его выходе. КромеRequest
Общий интерфейс PSR-7 также определяет другую структуру HTTP-сообщения, чтобы помочь нам во второй половине времени выполнения приложения:Response
. (Если вы действительно хотите понять эти детали, пожалуйста, прочтитеHTTP-сообщения и почему стандарт запросов и ответов PSR-7 такой облом. )
Исправлять HelloWorld
вернутьResponse
.// ...
namespace ExampleApp;
use Psr\Http\Message\ResponseInterface;
class HelloWorld
{
private $foo;
private $response;
public function __construct(
string $foo,
ResponseInterface $response
) {
$this->foo = $foo;
$this->response = $response;
}
public function __invoke(): ResponseInterface
{
$response = $this->response->withHeader('Content-Type', 'text/html');
$response->getBody()
->write("<html><head></head><body>Hello, {$this->foo} world!</body></html>");
return $response;
}
}
Затем измените контейнер наHelloWorld
предоставить новыйResponse
объект.// ...
use Middlewares\RequestHandler;
use Relay\Relay;
use Zend\Diactoros\Response;
use Zend\Diactoros\ServerRequestFactory;
use function DI\create;
// ...
$containerBuilder->addDefinitions([
HelloWorld::class => create(HelloWorld::class)
->constructor(get('Foo'), get('Response')),
'Foo' => 'bar',
'Response' => function() {
return new Response();
},
]);
$container = $containerBuilder->build();
// ...
Если вы обновите страницу сейчас, она будет пустой. Наше приложение возвращает правильное значение из планировщика промежуточного программного обеспечения.Response
Субъект, но... опухший?
Он вообще ничего не делает, вот и все.
Нам нужно упаковать еще одну вещь: пусковую установку. Передатчик находится между приложением и веб-сервером (Apache, nginx и т. д.) и отправляет ответ клиенту, делающему запрос. на самом деле получилосьResponse
объект и преобразовать его вAPI серверапонятная информация.
Супер Плюс наслаждайтесь! Пакет Zend Diactoros, который мы использовали для инкапсуляции запроса, также имеет встроенный передатчик для отправки ответов PSR-7.
Стоит отметить, что для примера мы просто экспериментируем с использованием передатчика. Хотя они могут быть немного сложнее, реальные приложения должны быть настроены как автоматические передатчики потоковой передачи для больших загрузок.Блог Zend показывает, как это сделать.
Исправлять public/index.php
, используется для получения от планировщикаResponse
, а затем передается на передатчик.
// ...
use Relay\Relay;
use Zend\Diactoros\Response;
use Zend\Diactoros\Response\SapiEmitter;
use Zend\Diactoros\ServerRequestFactory;
use function DI\create;
// ...
$requestHandler = new Relay($middlewareQueue);
$response = $requestHandler->handle(ServerRequestFactory::fromGlobals());
$emitter = new SapiEmitter();
return $emitter->emit($response);
Обновите браузер, и бизнес вернется! На этот раз мы использовали более надежный способ обработки ответа.
Строка 15 приведенного выше кода — это место, где заканчивается цикл запроса/ответа в нашем приложении, и где веб-сервер вступает во владение.
Переведено wilson_yang 17 часов назад 0 ретранслировать Зависит отSummerобзорЭпилог
Вот и все, мы сделали все. С помощью нескольких широко используемых, тщательно протестированных и надежных интерактивных компонентов мы реализовали современное PHP-приложение всего за 44 строки кода. это соответствуетPSR-4,PSR-7,PSR-11и PSR-15стандарт. Это означает, что вы можете использовать любой из вышеперечисленных стандартных совместимых HTTP-сообщений, контейнеров DI, промежуточного программного обеспечения и диспетчерских пакетов промежуточного программного обеспечения от различных поставщиков.Мы покопались в некоторых технологиях и выяснили, почему мы их выбрали, но я надеюсь, что вы получите представление о том, как легко построить полноценный фреймворк. Что еще более важно, я надеюсь, что вы сможете эффективно использовать их в своих существующих приложениях.
посмотри на этоПример в репозитории GitHub, вы можете раскошелиться или скачать его по желанию. Если вы ищете высококачественные, сильно развязанные ресурсы пакетов, я действительно рекомендуюAura,The League of Extraordinary Packages,Symfony components,Zend Framework components,Paragon Initiative's security-focused librariesи this list of PSR-15 middleware.Если вы планируете использовать образец кода в производственной среде, возможно, вы захотите провести рефакторинг маршрутов иконфигурация контейнера, и хранить их в своих собственных файлах, чтобы по мере увеличения сложности кодовой системы ее также было легко поддерживать. Также рекомендуетсяimplementing EmitterStack, который можно использовать для интеллектуальной обработки загрузки файлов и других объемных ответов.
Есть вопросы, сомнения или предложения по улучшению? добро пожаловать дразнить.
Переведено wilson_yang 7 часов назад 2 ретранслировать Зависит отSummerобзорВсе переводы в этой статье предназначены только для обучения и общения, при перепечатке обязательно указывайте переводчика, источник и ссылку на эту статью.
Наша работа по переводу следуетСС соглашение, если наша работа нарушает ваши права, пожалуйста, свяжитесь с нами вовремя.