I have a file which defines a name like this.
<root>/dir1/dir2/file1.php
//directory indicator
define("DI", "../../");
//adding required files
require_once DI . 'lib/Config.php';
require_once DI . 'lib/Common.php';
This adds Config.php and Common.php correctly.
Now when I try to get DI in Config.php like this
<root>/Config.php
<?php
class Config
{
public $value = DI . 'some_value';
}
I cant get that value there.
How can I make DI available inside Config class ?
EDIT
Real Folder Hierarchy
ROOT--> somefile.php
|__ lib --> config.php
|
|___ dir1
|
|__ dir2
|
|__ file1.php
I need to get root directory inside the class that is defined in config.php. that is I need to include somefile.php from config.php. I know I can do this simply like
include '../somefile.php';
But the problem is config.php holds a class which contains static methods. So I can get those methods like this.
Config::MethodInsideConfig();
Now when I try this from file1.php, it seems that ../somefile.php is trying to include from dir1. I think php uses file1.php's location for calculating the preceding directory. But what I need is it should get the file from root directory.
Inject the value as an argument into your class __construct(), and set the property in your constructor
class Config
{
public $value;
public __construct($di) {
$this->value = $di . 'some_value';
}
}
$myConfig = new Config(DI);
Why defining new constant PHP already has predefined constante DIR it's contain the current file directory.
So, your requires will be like this :
//adding required files
require_once __DIR__.'/../../'. '/lib/Config.php';
require_once __DIR__.'/../../'. 'lib/Common.php';
USING INI FILE
You can use config file (like config.ini)
[parameteres]
path_app_root="/usr/share/apache/www/my_app";
Then, your config as singleton class will be used to parse ini file and get config
<?php
class Config
{
public $configs = array();
private static $instance;
private function __construct() {}
public static function getInstance()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
self::$instance->configs = parse_ini_file(__DIR__."/../config.ini");
}
return self::$instance;
}
public function getRootPath()
{
return self::$instance->configs['path_app_root'];
}
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
Your file1.php will be like this
<?php
//adding required files
require_once '../../lib/Config.php';
echo Config::getInstance()->getRootPath();
Anas
Related
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?
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).
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).
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
Trying to understand how namespaces and autoload works on PHP
Server.php located at core/server.php
namespace core\server
{
class Main
{
public function getTopic()
{
$get_params = $_GET;
if (empty($get_params)) $get_params = ['subtopic' => 'test'];
return $get_params;
}
}
}
and Index.php
spl_autoload_register();
use core\server as subtopic;
$test = new subtopic\Main();
var_dump($test);
It cant load the class core/server/Main
Autoload doesn't work that way.
First I will explain how autoloaders works.
spl_autoload_register() is a function to register a function you have in your code to server as an autoloader, the standard function would be:
define('APP_PATH','/path/to/your/dir');
function auto_load($class)
{
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
The constant APP_PATH would be the path to your directory where your code lives.
As you noticed it the param that is passed to spl_autoload_register is the name of my function, this register the function so when a class is instanciated it runs that function.
Now an effective way to use autoloaders and namespaces would be the following:
file - /autoloader.php
define('APP_PATH','/path/to/your/dir');
define('DS', DIRECTORY_SEPARATOR);
function auto_load($class)
{
$class = str_replace('\\', DS, $class);
if(file_exists(APP_PATH.$class.'.php'))
{
include_once APP_PATH.$class.'.php';
}
}
spl_autoload_register('auto_load');
file - /index.php
include 'autoloader.php';
$tree = new Libs\Tree();
$apple_tree = new Libs\Tree\AppleTree();
file - /Libs/Tree.php
namespace Libs;
class Tree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
file - /Libs/Tree/AppleTree.php
namespace Libs\Tree;
class AppleTree
{
public function __construct()
{
echo 'Builded '.__CLASS__;
}
}
I'm using namespaces and autoload to load my functions in a nicely way, you can use namespace to describe in what dir your class resides and uses the autoloader magic to load it without any problems.
Note: I used the constant 'DS', because in *nix it uses '/' and in Windows it uses '\', with DIRECTORY_SEPARATOR we don't have to worry where the code is going to run, because it will be "path-compatible"
I have two folders in my directory:
Plugins
Classes
The Plugins folder contains two files: Sample.php and Plugins.php.
Sample.php is just a class with one function that extends the Plugins class. The Plugins class tries to create a new instance of the base class which is located in the Classes folder.
Plugins/Sample.php:
class Sample extends Plugins {
public $eggs;
public $pounds;
public function __construct() {
$this->eggs = "100";
$this->pounds = "10";
}
public function OnCall() {
echo "{$this->eggs} eggs cost {$this->pounds} pounds, {$this->name}!";
}
}
Plugins/Plugins.php:
class Plugins {
public $name;
public function __construct() {
include '../Classes/Base.php';
$base = new Base();
$this->name = $base->name;
}
}
Classes/Base.php:
class Base {
public $name = "Will";
public function Say() {
echo $this->name;
}
}
Index.php includes everything in the Plugins folder and is supposed to execute OnCall(). It is giving the following error messages:
Warning: include(../Classes/Base.php) [function.include]: failed to
open stream: No such file or directory in
/Applications/XAMPP/xamppfiles/htdocs/Plugins/Plugins/Plugins.php on
line 6
Warning: include() [function.include]: Failed opening
'../Classes/Base.php' for inclusion
(include_path='.:/Applications/XAMPP/xamppfiles/lib/php:/Applications/XAMPP/xamppfiles/lib/php/pear')
in /Applications/XAMPP/xamppfiles/htdocs/Plugins/Plugins/Plugins.php
on line 6
Fatal error: Class 'Base' not found in
/Applications/XAMPP/xamppfiles/htdocs/Plugins/Plugins/Plugins.php on
line 7
Index.php (if it helps):
foreach(glob('Plugins/*.php') as $file) {
require_once $file;
$class = basename($file, '.php');
if(class_exists($class)) {
$obj = new $class;
$obj->OnCall();
}
}
What I need to do is use the Base class in classes outside of the Classes folder. How can I do so?
You need to call the parent's constructor in your Sample class.
class Sample extends Plugins {
public $eggs;
public $pounds;
public function __construct() {
parent::__construct();
$this->eggs = "100";
$this->pounds = "10";
}
public function OnCall() {
echo "{$this->eggs} eggs cost {$this->pounds} pounds, {$this->name}!";
}
}
You probably want to take advantage of __autoload (http://ca1.php.net/manual/en/function.autoload.php)
Using this function will allow you to load up your classes easily no matter what directory they're in.
Simple Example:
function __autoload($classname) {
$path = "path/to/Classes/$classname.php";
if(file_exists($path)) {
require_once $path;
}
}
This means you would be able to remove your include statement from your Plugins class and simply keep the declaration $base = new Base(); and __autoload will be magically called and load up the correct file.
I'm creating an mvc structure for learning/teaching purpouses and so far I could set up the structure and a controller plus twig as template system.
The structure is:
index.php
controllers/
error.php
inc/
controller_base.php
view_manager.php
views/
.cache/
error/
view.html
So:
index instantiate twig autoloader (and mvc autoloader by spl_register).
index instantiate error controller inheriting controller_base.
controller_base is holding the view_manager.
error call view_manager to display the error/view.html and the only thing I get on the browser is error/view.html.
No errors on the apache log. (error_reporting(E_ALL))
Twig cache files created correctly, but the content doesn't look good to me:
protected function doDisplay(array $context, array $blocks = array()) {
// line 1
echo "error/view.html";
}
Anyone knows why, and how can I print the actual view?
Thanks in advance.
Code:
index.php: Declaring autoloaders
function __autoload($class_name)
{
if(file_exists("controllers/$class_name.php")):
include strtolower("controllers/$class_name.php");
elseif(file_exists("models/$class_name.php")):
include strtolower("models/$class_name.php");
elseif(file_exists("inc/$class_name.php")):
include strtolower("inc/$class_name.php");
endif;
}
spl_autoload_register('__autoload');
require_once 'vendor/autoload.php';
Twig_Autoloader::register(); has been avoided cause Twig installation was done by composer.
Adding it doesn't bring any change.
error.php (controller): called method.
public function show($param)
{
$this->viewMng->display(get_class().$data['view'], array())
}
controller_base.php:
class base
{
protected $viewMng;
public function __construct()
{
$this->viewMng = new viewmanager();
}
}
viewmanager.php: whole class
class viewmanager {
private $twig;
protected $template_dir = 'views/';
protected $cache_dir = 'views/.cache';
// protected $vars = array();
public function __construct($template_dir = null) {
if ($template_dir !== null) {
// Check here whether this directory really exists
$this->template_dir = $template_dir;
}
$loader = new Twig_Loader_String($this->template_dir);
$this->twig = new Twig_Environment($loader, array(
'cache' => $this->cache_dir));
}
public function render($template_file, $data = array()) {
if (!file_exists($this->template_dir.$template_file)) {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
return $this->twig->render($template_file, $data);
}
public function display($template_file, $data) {
if (!file_exists($this->template_dir.$template_file)) {
throw new Exception('no template file ' . $template_file . ' present in directory ' . $this->template_dir);
}
$tmpl = ($this->twig->loadTemplate($template_file));//print_r($tmpl);
$tmpl->display($data);
}
}
view.html:
<html><body> Hello </body></html>
The problem is based on the loader.
According to the Twig documentation:
Twig_Loader_String loads templates from strings. It's a dummy loader
as the template reference is the template source code This
loader should only be used for unit testing as it has severe
limitations: several tags, like extends or include do not make sense
to use as the reference to the template is the template source code
itself.
That's why it only prints the passed in string.
Twig_Loader_String, should be replaced by the proper loader.
In this case it works perfectly well Twig_Loader_Filesystem.
$loader = new Twig_Loader_Filesystem($this->template_dir);
This solve the problem and the MVC structure works completely fine.
Thanks taking a look, guys.
Given I have two User classes, one located in /model/User.php and the other located in /assets/User.php
Now I want to have a class which extend the User class from /model/User.php, yet it seems that php is always looking for the one located in /assets/User.php. Any idea how does php determines which class to extend from?
Use namespaces
user.php:
class User {
function someMethod(){}
}
myUser.php:
namespace My;
class User {
function someMethod(){}
}
And you can use it like this:
include 'user.php';
include 'myuser.php';
$user1 = new User();
$user2 = new \My\User();
You can use namespaces: PHP Manual
You should (if you use) setup appropriately your spl_autoload_register function (http://php.net/manual/en/function.spl-autoload-register.php) to be able autoload the objects in files with same name, bud the names of object can not be the same.
You can distinguish the objects by name and path in which the files are stored.
For instance, you require core class in your bootstrap.php and define autoloading function:
<?php // bootstrap.php
require_once Core.php;
spl_autoload_register(array('Core', 'auto_load'));
$myUserClass = new MyClasses_User() // the required files for the class are loaded because of auto_load, PHP determines the right classes due to naming conventions: DirName_ClassName
?>
<?php // Core.php
define('DIR_SEPARATOR', '/');
class Core {
// Autoloading
public static function auto_load($class, $dir_classes = 'classes')
{
$file = str_replace('_', DIR_SEPARATOR, $class); // replace _ by / for path to file
if ($path = find_file_based_on_class_name($dir_classes, $file))
{
// Load the class file
require $path;
}
}
?>
<?php
// classes/Model/User.php
class Model_User {
// define the class
}
?>
// classes/Assets/User.php
<?php
class Assets_User {
// define the class
}
?>
// classes/MyClasses/User.php
<?php
class MyClasses_User exdends Model_User {
// define the class
}
?>