Reputation: 43
I am creating a website and I am using Apache as my webserver.
I have created a website without the use of classes. All of my functions are located in one file and I just include this file on every page where I need to use some of the functions.
I am now wanting to swap to an OOP approach but I am having trouble understanding how to autoload my classes. I have been through a number of related pages, such as; PSR-4 Example, spl_autoload_register(), Related Question and I just can't seem to get my head around this.
So, as I am using Apache, the path for the root of my website is C:\Apache\htdocs.
My directories look as follows;
+ htdocs
+ Lib
+ Base
- User.php
- Device.php
- Company.php
- Database.php
+ Services
- UserService.php
- DeviceService.php
- CompanyServer.php
+ Config
- DbConfig.php
As an example so you can help me get my head around this, we will just talk about two classes DbConfig and Database.
DbConfig.php (with connection details omitted)
<?PHP
namespace Lib\Config;
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';
}
}
Database.php (Very simple until I can actually use it - at which point I will add its functionality)
<?PHP
namespace Lib\Base;
use Lib\Config\DbConfig;
class Database extends DbConfig
{
protected $connection;
private $dataSet;
private $sqlQuery;
public function __construct()
{
parent::__construct();
$this->connection = null;
$this->dataSet = null;
$this->sqlQuery = null;
}
}
So..
How do I load these classes, using some implementation of spl_autoload_register(), into another .php file so that I may use them to create objects? for example in a file named 'Testing.php'
Previously I would parse a .ini file outside of the root of my folder to get the database connection details, should I do this with my new approach of having the connection details in the class DbConfig?
Upvotes: 4
Views: 1457
Reputation: 8968
Here is a quick example I knocked together:
Create a file called autoloader.php
. This example was taken from PSR-4 Examples. Change Acme
to your projects name.
This will allow you to use namespaces, so long as they are stored under Lib/
and you add the namespaces to each file (as you're already doing).
<?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;
}
});
This uses spl_autoload_register to handle autoloading of dependencies/classes, and also allows for the use of PRS-4 namespaces.
Next, your Database
class would be stored in 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'
]
];
}
}
And finally, your index page includes the autoloader.php
script, which takes care of automatically included the classes.
<?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());
And finally, you asked about configuration files. I use a config file like so:
<?php
return [
'MYSQL' => [
'USERNAME' => 'root',
'PASSWORD' => '',
'DATABASE' => 'test',
'HOST' => 'localhost'
]
];
Which allows you to include the config file with a simple:
$config = require 'config.php';
And access like so:
$config['MYSQL']['USERNAME'];
I pass the \PDO
database connection as a dependency into the Database
class as an example. This is called Dependency Injection.
Another example: Lib/Services/UserService.php
:
<?php
namespace Acme\Services;
class UserService
{
...
}
You can now call this in code (provided the autoloader is included) like so:
$userService = new \Acme\Services\UserService;
I'd also recommend checking out Composer. Super helpful if you want to use public packages from Packagist and it's very easy to create your own. Supports PSR-* autoloading too (PSR4 being most common).
Upvotes: 1
Reputation: 2284
register a autoload function:
add the code in any file could be included before you use other php library classes, for example in your project entry file.
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);
not recommend to use namespace if your project is not big enough, just use directory and file name to do it
Upvotes: 0
Reputation: 17352
According to the PSR-4 standard,
The fully qualified class name MUST have a top-level namespace name, also known as a "vendor namespace".
Your classes don't seem to have this. To add it, change namespace Lib\Config;
to, for example, namespace AskMeOnce\Lib\Config;
. You'll need to add this prefix to all your classes.
Then, using the PSR-4 Autoloader you referenced, change $prefix = 'Foo\\Bar\\';
to $prefix = 'AskMeOnce\\';
and $base_dir = __DIR__ . '/src/';
to $base_dir = 'C:\Apache\htdocs';
. Put that function in a file called autoload.php, and require it in any file that needs to know how to autoload.
Once you require
that autoloader, PHP will know to look for any class that begins with the namespace AskMeOnce
in the specified base directory. After removing that prefix from the namespace, the namespace represents the rest of the path. For example, AskMeOnce\Lib\Config\DbConfig
Will be looked for at <basedir>/Lib/Config/DbConfig.php
.
To answer your questions specifically, (1) Just put the function in autoload.php
, with the modifications I mentioned above. And (2) since the ini file is not a PHP class, it doesn't matter where it is as long as your code knows how to find it (whether this is a hard-coded path or otherwise).
Upvotes: 1