phpframeworksautoloadphpdotenv

How to use phpdotenv library on my whole project?


I started a project using composer and donlowaded the package vlucas/phpdotenv. I would like to call the $_ENV['name'] for example in my whole project without needing to instantiate the dot env in every class like that:

$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();

How can I achieve that like a laravel framework does for example?

Another question is I tough I needed to import my autoload require __DIR__.'/../vendor/autoload.php'; in order to call use Dotenv\Dotenv;...

How is that I can use Dotenv without the require autoload in my classes?

EDIT added index.php:

<?php

use Dotenv\Dotenv;

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

$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();

How do I autostart it now?


Solution

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