PHP: проблемы с автоматической загрузкой


Я создаю веб-сайт и использую Apache в качестве своего веб-сервера.

Я создал сайт без использования классов. Все мои функции находятся в одном файле, и я просто включаю этот файл на каждой странице, где мне нужно использовать некоторые функции.

Сейчас я хочу перейти на подход ООП, но у меня возникли проблемы с пониманием того, как загружать мои классы автоматически. Я просмотрел ряд связанных страниц, таких как; пример PSR-4 , spl_autoload_register(), связанный с этим Вопрос , и я просто не могу понять, как это происходит.

Итак, поскольку я использую Apache, путь к корню моего веб-сайта является C:Apachehtdocs .

Мои каталоги выглядят следующим образом;

+ htdocs
    + Lib
        + Base
            - User.php
            - Device.php
            - Company.php
            - Database.php
        + Services
            - UserService.php
            - DeviceService.php
            - CompanyServer.php
        + Config
            - DbConfig.php

В качестве примера, чтобы вы могли помочь мне разобраться в этом, мы просто поговорим о двух классах DbConfig и Database.

DbConfig.php (с деталями подключения опущено)

<?PHP
namespace LibConfig;

class DbConfig
{
    protected $serverName;
    protected $userName;
    protected $password;
    protected $dbName;

   public function __construct()
    {
        $this->serverName = 'not my server name';
        $this->userName = 'not my username';
        $this->passCode = 'not my password';
        $this->dbName = 'not my database';
    }
}

база данных.php (Очень простой, пока я не смогу его использовать-в этот момент я добавлю его функциональность)

<?PHP
namespace LibBase;

use LibConfigDbConfig;

class Database extends DbConfig
{
    protected $connection;
    private $dataSet;
    private $sqlQuery;

    public function __construct()
    {
        parent::__construct();      
        $this->connection = null;
        $this->dataSet = null;
        $this->sqlQuery = null;
    }
}

Итак..

  1. Как я загружаю эти классы, используя некоторую реализацию spl_autoload_register () , в другой .php файл, чтобы я мог использовать их для создания объектов? например, в файле с именем 'тестирование.php'

  2. Ранее я бы разбирал a .ini файл за пределами чтобы получить сведения о соединении с базой данных, я должен сделать это с помощью моего нового подхода, имеющего сведения о соединении в классе DbConfig ?

3 4

3 ответа:

Согласно стандарту PSR-4,

Полное имя класса должно иметь имя пространства имен верхнего уровня, также известное как"пространство имен поставщика".

Похоже, в ваших классах этого нет. Чтобы добавить его, измените namespace Lib\Config;, например, на namespace AskMeOnce\Lib\Config;. Вам нужно будет добавить этот префикс ко всем вашим классам.

Затем, используя автозагрузчик PSR-4, на который вы ссылались, измените $prefix = 'Foo\\Bar\\'; на $prefix = 'AskMeOnce\\'; и $base_dir = __DIR__ . '/src/'; на $base_dir = 'C:\Apache\htdocs';. Поместите эту функцию в файл с именем autoload.php, и требовать его в любой файл, который должен знать, как выполнить автоматическую загрузку.

После того, как Вы require этот загрузчик, PHP будет знать, чтобы искать любой класс, который начинается с пространства имен AskMeOnce в указанном базовом каталоге. После удаления этого префикса из пространства имен пространство имен представляет собой остальную часть пути. Например, AskMeOnce\Lib\Config\DbConfig будет искать в <basedir>/Lib/Config/DbConfig.php.

Чтобы ответить на ваши вопросы конкретно, (1) Просто поместите функцию в autoload.php с изменениями, о которых я упоминал выше. И (2) поскольку ini-файл является не класс PHP, не имеет значения, где он находится, пока ваш код знает, как его найти (является ли это жестко закодированный путь или нет).

Вот краткий пример, который я собрал вместе:

Создайте файл с именем autoloader.php. Этот пример был взят из примеровPSR-4 . Измените Acme на имя вашего проекта.

Это позволит вам использовать пространства имен, пока они хранятся в Lib/, и вы добавляете пространства имен в каждый файл (как вы уже делаете).

<?php
/**
 * An example of a project-specific implementation.
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {

    // project-specific namespace prefix
    $prefix = 'Acme\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/Lib/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
    print $file;
    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }
});

Это использует spl_autoload_register для обработки автоматической загрузки зависимостей / классов, а также позволяет использовать PRS-4 пространство имен.

Далее, ваш класс Database будет сохранен в Lib/Base/Database.php.

<?php

namespace Acme\Base;

class Database
{
    private $db;

    public function __construct(\PDO $db)
    {
        $this->db = $db;
    }

    public function allUsers()
    {
        /*
         * Query to fetch all users from the database
         */

        return [
            [
                'username' => 'Bob',
                'role' => 'member'
            ],
            [
                'username' => 'Joe',
                'role' => 'admin'
            ]
        ];
    }
}
И, наконец, ваша индексная страница включает в себя скрипт autoloader.php, который заботится о автоматически включенных классах.
<?php

require_once 'autoloader.php';

$config = require 'config.php';

try {
    $dbc = new PDO("mysql:dbname={$config['MYSQL']['DATABASE']};host={$config['MYSQL']['HOST']}",
                   $config['MYSQL']['USERNAME'], $config['MYSQL']['PASSWORD']);
} catch (PDOException $e) {
    die('Could not connect to database ' . $e->getMessage());
}

$db = new \Acme\Database($dbc);
print_r($db->allUsers());

И, наконец, вы спросили о файлах конфигурации. Я использую конфигурационный файл так:

<?php

return [
    'MYSQL' => [
        'USERNAME' => 'root',
        'PASSWORD' => '',
        'DATABASE' => 'test',
        'HOST' => 'localhost'
    ]
];

, что позволяет включить конфигурационный файл с простым:

$config = require 'config.php';

И доступ Вот так:

$config['MYSQL']['USERNAME'];

Я передаю соединение с базой данных \PDO как зависимость в класс Database в качестве примера. Это называетсяинъекцией зависимостей .

Еще один пример: Lib/Services/UserService.php:

<?php

namespace Acme\Services;

class UserService
{
    ...
}

Теперь вы можете вызвать это в коде (при условии, что автозагрузчик включен) следующим образом:

$userService = new \Acme\Services\UserService;

Я бы также рекомендовал проверить Composer. Очень полезно, если вы хотите использовать общедоступные пакеты из Packagist, и очень легко создать свой собственный. Поддерживает автозагрузку PSR - * (PSR4 наиболее распространен).

Зарегистрируйте функцию автоматической загрузки:

Добавить код в любой файл можно перед использованием других классов библиотеки php, например, в файле ввода проекта.

defined('ROOT') or define('ROOT', 'C:/Apache/htdocs');
// define root directory
// or `defined('ROOT') or define('ROOT', __DIR__)` if
// the file is in the root directory

spl_autoload_register(function($name) {
    $file = ROOT . "/{$name}.php";
    if(!is_file($file)) {
        return false;
    } else {
        return include $file;
    }
}, false, true);

Не рекомендуется использовать пространство имен, если ваш проект недостаточно велик, просто используйте имя каталога и файла, чтобы сделать это