📜 ⬆️ ⬇️

Modern PHP without frameworks


I have a difficult task for you. The next time you start a new project, try to do without the PHP framework. I'm not going to list the flaws of the frameworks, and this is not a manifestation of the rejection of someone else's development : in this guide we will use packages written by developers of several frameworks. I fully respect innovation in this area.


But this article is not about them. She is about you. About the possibility of becoming a better developer.


Perhaps the main advantage of rejecting the framework will be knowing how everything works under the hood. You will see what is happening without relying on a framework that cares about you so much that you cannot debug something or fully understand it.


Perhaps your next job will not allow you to enjoy the launch of a new project without a framework. Many important business-critical PHP tasks involve the use of existing applications. It doesn’t matter if this application is built on a modern framework like Laravel or Symfony, on one of the old platforms like CodeIgniter or FuelPHP - or this is depressingly widespread PHP application with “include-oriented architecture” : if now you will develop without a framework , you will be better prepared for any future PHP project.


Previously, they tried to create without frameworks because some systems are forced to interpret and route HTTP requests, send HTTP responses and manage dependencies. The lack of standards inevitably led to the fact that at least these framework components were closely interlinked. So if you started to develop a project without a framework, then in the end you came to create your own framework.


But today, thanks to the efforts of PHP-FIG in the field of autoload and interoperability, you can develop without a framework without creating it in the same way. There are many great, mutually compatible packages written by numerous developers. And to collect them into a single system is much easier than you think!


How does PHP work?


First of all, it is important to understand how PHP applications interact with the outside world.


PHP executes server applications in a request / response loop. All interaction with the application - from the browser, the command line or the REST API - comes to it as requests. When a request is received, the application loads, processes the request and generates a response that is sent back to the client, and the application closes. And this happens with every conversion.


Query controller


Armed with this knowledge, let's start with the front controller. It is a PHP file that handles all requests to your application. That is, this is the first PHP file that the request gets into, and (in fact) the last PHP file that the application’s response goes through.


Let's use the classic example of Hello, world !, Served by a PHP web server built into it , to check if everything is set up correctly. If you haven’t already done this, make sure that PHP 7.1 or higher is installed in the environment.


, public, — index.php :


<?php
declare(strict_types=1);

echo 'Hello, world!';

, — PHP- , — (type hinting) , .


( Terminal MacOS) PHP -.


php -S localhost:8080 -t public/

http://localhost:8080/. Hello, world! ?


. !



PHP, , , include require 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 . include, .


<?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 echo announce HelloWorld.


// ...

require_once dirname(__DIR__) . '/vendor/autoload.php';

$helloWorld = new \ExampleApp\HelloWorld();
$helloWorld->announce();

!


?


— , , , - .


, . . , .


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. ( , , - .)



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();

, , .


, , , . .


https://kevinsmith.io/modern-php-without-a-framework-middleware


Middleware


, , , middleware — , , , - . , - .


, . , , .


:



— ? . middleware / , .


: .



, , (, URI /products/purple-dress/medium ProductDetails::class purple-dress medium).


FastRoute , PSR-15.


middleware


- , .


PSR-15 — , middleware ( « »), . , PSR-15, middleware.


Relay.


composer require relay/relay:2.x@dev

PSR-15 , HTTP-, PSR-7, Zend Diactoros.


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());

16 ServerRequestFactory::fromGlobals() , Relay. .


FastRoute (FastRoute , , , ).


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

exit; __invoke(). , .


http://localhost:8080/hello !


,


, DI-, , . .


?


, — — 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());

! Hello, bar world!.



, exit HelloWorld?


, , . HelloWorld — , — , , , HelloWorld.


, , ( ) . Request PSR-7 HTTP-, : Response. , HTTP- , PSR-7 Request Response.


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 / -.



44 , , , bootstrap PHP-. PSR-4, PSR-7, PSR-11 PSR-15, HTTP-, DI-, middleware .


, , . , .


, .


, Aura, The League of Extraordinary Packages, Symfony, Zend Framework, Paragon Initiative PSR-15 middlleware.


production, , , , . EmitterStack «» .


')

Source: https://habr.com/ru/post/352122/


All Articles