PROJECT SETUP
Setup the project and start built-in php server
mkdir /var/www/php/noframework
cd /var/www/php/noframework
mkdir public
echo "<?= 'Hello World' ?>" > public/index.php
php -S localhost:8080 -t /var/www/php/noframework
// http://localhost:8080/public
// Hello World
// Ctrl+C (stop the server)
// Failed to listen on localhost:8080 (reason: Address already in use)
// killall -9 php # stop php server
Initialize Composer for the project and add src/ to autoload.
Composer install will generate autoload files and will bring in any dependencies.
composer init
// Add ExampleApp namespace to composer.json
{
"name": "vendor/minte9",
"type": "project",
"require": {},
"autoload": {
"psr-4": {
ExampleApp\\": "src/"
}
}
}
// install composer
mkdir src/
composer install
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ExampleApp\\' => array($baseDir . '/src'),
);
// /public/index.php
declare(strict_types=1);
use ExampleApp\HelloWorld;
require_once __DIR__.'/../vendor/autoload.php';
$obj = new HelloWorld();
$obj->test();
// /src/HelloWorld.php
declare(strict_types=1);
namespace ExampleApp;
class HelloWorld
{
public function test() : void
{
echo "Hello World - Composer autoload";
}
}
// http://localhost:8080/public
// Hello World - Composer autoload
DEPENDENCY INJECTION
As an example, the UserManager takes the Mailer as a constructor parameter. This is dependency injection!
declare(strict_types=1);
class Mailer
{
}
class UserManager
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
}
$mailer = new Mailer();
$userManager = new UserManager($mailer); // Look Here
If we add the pdo connection inside the method, to the class is coupled to both app and db.
This is messy!
class HelloWorld
{
...
public doSomething()
{
// $dbConnection = new \PDO();
}
}
Add PDO as a dependency to the HelloWorld Class.
This lot cleaner, easier to understand and unit test.
// public/index.php
declare(strict_types=1);
use ExampleApp\HelloWorld;
require_once __DIR__.'/../vendor/autoload.php';
$dsn = "mysql:host=localhost;dbname=sys;charset=utf8mb4;port=3306";
$pdo = new PDO($dsn, "admin", "password");
$obj = new HelloWorld($pdo);
$obj->test();
// /src/HelloWorld.php
declare(strict_types=1);
namespace ExampleApp;
class HelloWorld
{
private $dbConnection;
public function __construct(\PDO $dbConnection)
{
$this->db = $dbConnection;
}
public function test()
{
echo "Hello World - autoload & PDO <br>";
$stmt = $this->db->query("SELECT * FROM sys_config");
while($row = $stmt->fetch()) {
var_dump($row);
}
}
}
CONTAINER
A dependency injection container wraps around your entire application. It helps a lot as your application grows and became more complex. One of the most popular DI container is PHP-DI.
composer require php-di/php-di
// /public/index.php
declare(strict_types=1);
use ExampleApp\HelloWorld;
use DI\ContainerBuilder;
use function DI\create;
use function DI\get;
require_once __DIR__.'/../vendor/autoload.php';
$containerBuilder = new ContainerBuilder();
$containerBuilder->useAutowiring(false);
$containerBuilder->useAnnotations(false);
$containerBuilder->addDefinitions([
HelloWorld::class => function() { // Look Here
$dsn = "mysql:host=localhost;dbname=sys;charset=utf8mb4;port=3306";
$pdo = new PDO($dsn, "admin", "password");
return new HelloWorld($pdo);
}
]);
$container = $containerBuilder->build();
$obj = $container->get(HelloWorld::class);
$obj->test(); // Hello World - autoloaded & PDO & DI
// /src/HelloWorld.php
declare(strict_types=1);
namespace ExampleApp;
class HelloWorld
{
private $db;
public function __construct(\PDO $pdo)
{
$this->db = $pdo;
}
public function test() : void
{
echo "Hello World - autoloaded & PDO & DI <br>";
$stmt = $this->db->query("SELECT * FROM sys_config");
$row = $stmt->fetch();
echo $row['variable']; // diagnostics.include_raw
}
}
This looks like a lot of extra fuss for what we already did.
The container will prove usefull when we add other tools to our application.
Last update: 465 days ago