Normally, you have a bootstrap file where you include both Composer's autoload.php
and other common stuff, e.g. the instantiation of Dotenv
. This can be an index.php
file which handles all incoming requests (the most common pattern you will find in frameworks, Laravel does the same: https://github.com/laravel/laravel/blob/8e5510458e1a4c0bf4b78024d9b0cf56102c8207/public/index.php#L34) or some other file you will include in all your .php
files that are handling requests (if you are not using a common index.php
).
If you want to learn more about how frameworks work, I suggest you browse & read their code. You can start with something smaller (like Slim).
LATER EDIT - DUMMY TUTORIAL:
Alright, let's build a very basic project:
Step 1. We start with the following:
composer.json
{
"name": "example/project",
"type": "project",
"require": {
"vlucas/phpdotenv": "^5.3"
}
}
index.php
<?php
use Dotenv\Dotenv;
require __DIR__.'/vendor/autoload.php';
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
echo 'Hello world!';
var_dump($_ENV);
.env
SAMPLE_ENV_VAR = some_value
The file structure you have after you run composer install
:
project_root_folder/
- vendor/
- .env
- composer.json
- composer.lock
- index.php
Given this state, when you visit your index.php in the browser (let's say http://localhost/ or http://localhost/index.php), it should run without errors and you should also see the custom env var we have declared in the .env
file.
Step 2. Add basic routing:
index.php
<?php
use Dotenv\Dotenv;
require __DIR__.'/vendor/autoload.php';
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Figure out the requested page; fallback to the home page.
$page = $_GET['page'] ?? 'Home';
if ($page == 'Home') {
echo 'This is the home page.';
}
elseif ($page == 'Books/index') {
echo 'This is the books index page.';
}
elseif ($page == 'Books/detail') {
echo 'This is the detail page for book with ID: ' . $_GET['book_id'];
}
else {
echo 'NOT FOUND :(';
}
If you now visit http://localhost/ or http://localhost/index.php or http://localhost/index.php?page=Home, you should see the home page.
If you visit http://localhost/index.php?page=Books/index, you should see the books index page and so on...
Step 3. Move pages' logic out to separate controllers:
index.php
<?php
use Dotenv\Dotenv;
require __DIR__.'/vendor/autoload.php';
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Figure out the requested page; fallback to the home page.
$page = $_GET['page'] ?? 'Home';
// We require the necessary controller class file and call its appropriate method:
if ($page == 'Home') {
require __DIR__.'/src/controllers/Home.php';
(new Home())->index();
}
elseif ($page == 'Books/index') {
require __DIR__.'/src/controllers/Books.php';
(new Books())->index();
}
elseif ($page == 'Books/detail') {
require __DIR__.'/src/controllers/Books.php';
(new Books())->detail();
}
else {
echo 'NOT FOUND :(';
}
src/Controllers/Home.php
<?php
class Home
{
public function index()
{
echo 'This is the home page.';
}
}
src/Controllers/Books.php
<?php
class Books
{
public function index()
{
echo 'This is the books index page.';
}
public function detail()
{
echo 'This is the detail page for book with ID: ' . $_GET['book_id'];
}
}
The new file structure:
project_root_folder/
- src/
- Controllers/
- Home.php
- Books.php
- vendor/
- .env
- composer.json
- composer.lock
- index.php
Step 4. Make use of composer and PSR-4 autoloading (in order to get rid of the manual requires):
Notice the additions to composer.json
and the added namespaces to the .php
files.
composer.json
{
"name": "example/project",
"type": "project",
"require": {
"vlucas/phpdotenv": "^5.3"
},
"autoload": {
"psr-4": {
"Example\\Project\\": "src/"
}
}
}
Make sure to run composer dump-autoload
in order to "apply" the above changes.
src/Controllers/Home.php
<?php
namespace Example\Project\Controllers;
class Home
{
public function index()
{
echo 'This is the home page.';
}
}
src/Controllers/Books.php
<?php
namespace Example\Project\Controllers;
class Books
{
public function index()
{
echo 'This is the books index page.';
}
public function detail()
{
echo 'This is the detail page for book with ID: ' . $_GET['book_id'];
}
}
index.php
<?php
use Dotenv\Dotenv;
require __DIR__.'/vendor/autoload.php';
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Figure out the requested page; fallback to the home page.
$page = $_GET['page'] ?? 'Home';
if ($page == 'Home') {
(new \Example\Project\Controllers\Home())->index();
}
elseif ($page == 'Books/index') {
(new Example\Project\Controllers\Books())->index();
}
elseif ($page == 'Books/detail') {
(new Example\Project\Controllers\Books())->detail();
}
else {
echo 'NOT FOUND :(';
}
Step 5. Load & call the controllers dynamically.
index.php
<?php
use Dotenv\Dotenv;
require __DIR__.'/vendor/autoload.php';
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// Figure out the requested page; fallback to the home page.
$page = $_GET['page'] ?? 'Home';
// Figure out the controller name and the method name.
if (strpos($page, '/') === FALSE) {
$page = $page . '/index';
}
[$controllerName, $method] = explode('/', $page);
$controllerClassName = '\\Example\\Project\\Controllers\\' . $controllerName;
if (class_exists($controllerClassName) && method_exists($controllerClassName, $method)) {
(new $controllerClassName())->$method();
}
else {
echo 'NOT FOUND :(';
}
After this change we can introduce new controllers (e.g. Faq
) without the need to modify index.php
anymore. Implementing the controller class will be sufficient for the http://localhost/index.php?page=Faq URL to work.
... and so on.
The above is a simple and incomplete example for routing. A complete routing mechanism is much more complex than this, nevertheless it can provide you basic understanding on how to use composer, auto-loading, routing.