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.