PHP without a framework: Difference between revisions

From Rixort Wiki
Jump to navigation Jump to search
 
(37 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Things to think about ==
* How to inject a request and eject a response.
* How to create a database connection based on config.
* How to upgrade the framework without touching the rest of the application.
* How to access the rest of the application from the vendor tree.
== Limitations ==
* MySQL/MariaDB the only supported RDBMS.
* UTF-8 everywhere.
== Dependencies ==
== Dependencies ==


Line 4: Line 16:


* '''Dependency injection:''' [https://php-di.org/ PHP-DI]
* '''Dependency injection:''' [https://php-di.org/ PHP-DI]
* '''PSR-15 compatible dispatcher:'''
* '''PSR-15 middleware dispatcher:''' [https://route.thephpleague.com/ Route]
* '''PSR-7 implementation:'''
* '''PSR-7 implementation:''' [https://docs.laminas.dev/laminas-diactoros/ laminas-diactoros]
* '''Error handling:''' [https://github.com/filp/whoops whoops]
* '''Error handling:''' [https://github.com/filp/whoops whoops] with symfony/var-dumper
* '''Templates:''' [https://twig.symfony.com/ Twig]
* '''Logging:''' monolog/monolog
* '''Date/Time manipulation:''' nesbot/carbon
 
For configuration variables, use a simple PHP script which returns an array and is brought in using <code>require_once</code>. This should work across all versions of PHP and if anyone accesses the file directly they will be shown a blank page (but it is still a good idea to keep it outside the document root and not in version control).
 
== Databases ==
 
Use PDO for everything, as this gives the maximum flexibility.
 
* Set the charset in the DSN.
* Exceptions as the error mode (you should never get an SQL error in normal operation, so an exception is appropriate).
* Always use <code>bindValue</code> and set the data type (third parameter).
* Set fetch mode to <code>PDO::FETCH_ASSOC</code>.
* Remember <code>beginTransaction</code> and <code>commit</code> are available.
 
== Testing ==
 
Iñtërnâtiônàlizætiøn is a good test string.


== PHP built-in server ==
== PHP built-in server ==
Line 21: Line 52:
== Instructions ==
== Instructions ==


Initialise composer project:
=== Add dependencies ===


  composer init
  composer require php-di/php-di
composer require filp/whoops
composer require laminas/laminas-diactoros
composer require laminas/laminas-httphandlerrunner
composer require league/route


Create skeleton directory structure:
Create skeleton directory structure:
Line 29: Line 64:
  mkdir public src
  mkdir public src


Add dependencies:
== Bootstrap file ==
 
composer require php-di/php-di
composer require filp/whoops
 
Run composer to install dependencies and create <code>vendor</code>:
 
composer install


Create Bootstrap file at <code>src/Bootstrap.php</code>:
Create Bootstrap file at <code>src/Bootstrap.php</code>:
Line 43: Line 71:
   
   
  declare(strict_types = 1);
  declare(strict_types = 1);
error_reporting(E_ALL);
date_default_timezone_set('Europe/London');
   
   
  require_once __DIR__ . '/../vendor/autoload.php';
  require_once __DIR__ . '/../vendor/autoload.php';
Line 49: Line 80:
  $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
  $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
  $whoops->register();
  $whoops->register();
== Front controller ==


Create skeleton front controller at <code>public/index.php</code>:
Create skeleton front controller at <code>public/index.php</code>:
Line 73: Line 106:
   }
   }
  }
  }
== Static Page Controller ==
<?php
declare(strict_types = 1);
namespace WebApp\Controllers;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Laminas\Diactoros\Response;
use MicroFramework\Config;
use MicroFramework\View;
class StaticPageController extends \MicroFramework\Controller
{
    public function index(ServerRequestInterface $request): ResponseInterface
    {
        $response = new Response;
        $config = Config::getInstance()->getData();
        $html = View::render($config['views']['templates_path'], 'index.html');
        $response->getBody()->write($html);
        return $response;
    }
}
== Logins and registration ==
* <code>password_hash</code>
* <code>password_verify</code>
* <code>password_needs_rehash</code>
[[Category:PHP]]

Latest revision as of 13:57, 4 April 2021

Things to think about

  • How to inject a request and eject a response.
  • How to create a database connection based on config.
  • How to upgrade the framework without touching the rest of the application.
  • How to access the rest of the application from the vendor tree.

Limitations

  • MySQL/MariaDB the only supported RDBMS.
  • UTF-8 everywhere.

Dependencies

The following dependencies are all actively maintained and available via Composer:

  • Dependency injection: PHP-DI
  • PSR-15 middleware dispatcher: Route
  • PSR-7 implementation: laminas-diactoros
  • Error handling: whoops with symfony/var-dumper
  • Templates: Twig
  • Logging: monolog/monolog
  • Date/Time manipulation: nesbot/carbon

For configuration variables, use a simple PHP script which returns an array and is brought in using require_once. This should work across all versions of PHP and if anyone accesses the file directly they will be shown a blank page (but it is still a good idea to keep it outside the document root and not in version control).

Databases

Use PDO for everything, as this gives the maximum flexibility.

  • Set the charset in the DSN.
  • Exceptions as the error mode (you should never get an SQL error in normal operation, so an exception is appropriate).
  • Always use bindValue and set the data type (third parameter).
  • Set fetch mode to PDO::FETCH_ASSOC.
  • Remember beginTransaction and commit are available.

Testing

Iñtërnâtiônàlizætiøn is a good test string.

PHP built-in server

The PHP built-in server is sufficient for most purposes. Create a configuration file, local.ini:

error_reporting = E_ALL
display_errors = On

Start the server:

php -S localhost:8000 -t public -c local.ini

Instructions

Add dependencies

composer require php-di/php-di
composer require filp/whoops
composer require laminas/laminas-diactoros
composer require laminas/laminas-httphandlerrunner
composer require league/route

Create skeleton directory structure:

mkdir public src

Bootstrap file

Create Bootstrap file at src/Bootstrap.php:

<?php

declare(strict_types = 1);
error_reporting(E_ALL);

date_default_timezone_set('Europe/London');

require_once __DIR__ . '/../vendor/autoload.php';

$whoops = new Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();

Front controller

Create skeleton front controller at public/index.php:

<?php

declare(strict_types = 1);

require_once __DIR__ . '/../vendor/autoload.php';

Create HelloWorld class in src/HelloWorld.php:

<?php

declare(strict_types = 1);

namespace MyApp;

class HelloWorld
{
  public function hello() : void
  {
    echo 'Hello World';
  }
}


Static Page Controller

<?php

declare(strict_types = 1);

namespace WebApp\Controllers;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

use Laminas\Diactoros\Response;

use MicroFramework\Config;
use MicroFramework\View;

class StaticPageController extends \MicroFramework\Controller
{
    public function index(ServerRequestInterface $request): ResponseInterface
    {
        $response = new Response;
        $config = Config::getInstance()->getData();
        $html = View::render($config['views']['templates_path'], 'index.html');
        $response->getBody()->write($html);

        return $response;
    }
}

Logins and registration

  • password_hash
  • password_verify
  • password_needs_rehash