Overschrijf variable in autoloader
Ik heb vraag omtrent het gebruik van de __autloader functie.
Dit is een stukje uit mijn script:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function __autoload($class)
{
strtolower($class);
if (file_exists(PAF_FRAMEWORK_ROOT . '\class.' . $class . '.php'))
{
require_once PAF_FRAMEWORK_ROOT . '\class.' . $class . '.php';
}
$request = str_replace("com_", "", $_GET['component']);
$filename = str_replace($request, "", $class);
strtolower($filename);
if (file_exists(APPLICATION_ROOT . '\components\com_' . $request . '/' . $filename . '.php'))
{
require_once APPLICATION_ROOT . '\components\com_' . $request . '/' . $filename . '.php';
}
}
$login = 0;
if($login == 0)
{
$model = new loginModel();
$controller = new loginController($model);
$view = new loginView($controller, $model);
}
if($login == 1)
{
// Convert GET
$request = str_replace("com_", "", $_GET['component']);
// Set mvc vars
$componentModel = $request . 'Model';
$componentController = $request . 'Controller';
$componentView = $request . 'View';
// Include classes
$model = new $componentModel();
$controller = new $componentController($model);
$view = new $componentView($controller, $model);
if (isset($_GET['action']) && !empty($_GET['action']))
{
$controller->{$_GET['action']}();
}
}
{
strtolower($class);
if (file_exists(PAF_FRAMEWORK_ROOT . '\class.' . $class . '.php'))
{
require_once PAF_FRAMEWORK_ROOT . '\class.' . $class . '.php';
}
$request = str_replace("com_", "", $_GET['component']);
$filename = str_replace($request, "", $class);
strtolower($filename);
if (file_exists(APPLICATION_ROOT . '\components\com_' . $request . '/' . $filename . '.php'))
{
require_once APPLICATION_ROOT . '\components\com_' . $request . '/' . $filename . '.php';
}
}
$login = 0;
if($login == 0)
{
$model = new loginModel();
$controller = new loginController($model);
$view = new loginView($controller, $model);
}
if($login == 1)
{
// Convert GET
$request = str_replace("com_", "", $_GET['component']);
// Set mvc vars
$componentModel = $request . 'Model';
$componentController = $request . 'Controller';
$componentView = $request . 'View';
// Include classes
$model = new $componentModel();
$controller = new $componentController($model);
$view = new $componentView($controller, $model);
if (isset($_GET['action']) && !empty($_GET['action']))
{
$controller->{$_GET['action']}();
}
}
Het probleem is dat het $request variable in de __autloader functie, de $_GET['component'] uit mijn URL gebruikt. Deze wil ik overschrijven als de gebruiker niet in ingelogd. Op dit moment krijg ik namelijk nog een 'Class 'loginModel' not found' error omdat de $_GET['component'] nog op een andere component staat. Hierdoor zoekt de autloader de bestanden voor het login formulier in de foute map.
Weet iemand een methode dit voor elkaar te krijgen?
Alvast bedankt!
Groeten,
Niels
Gewijzigd op 01/05/2015 09:29:07 door Niels Veer
Een autoloader is enkel bedoeld om classes / libraries (die in een eigen .php bestand staan) automatisch te includen zodat je niet elke keer een include regel hoeft toe te voegen. Daarnaast gaat een autoloader hand in hand met namespaces. Ik stel dan meteen voor om de psr0 standaard te gebruiken. Leesvoer: http://www.sitepoint.com/autoloading-and-the-psr-0-standard/
Gewijzigd op 01/05/2015 11:49:20 door Frank Nietbelangrijk
- er wordt afgeraden __autoload() te gebruiken, gebruik in plaats hiervan spl_autoload_register()
- alle diskoperaties (zoals file_exists) zijn relatief duur, daarnaast moet je er van uit kunnen gaan dat de bestanden bestaan, en anders moet je dit met foutafhandeling ondervangen, maar vermijd het gebruik van disk operaties in je autoloader!
- de volgorde van het zoeken naar classes kun je beinvloeden door de volgorde van de uitgangspaden-voor-invoegen te veranderen via set_include_path();
Ik weet niet of dit in het bovenstaande geval de beste / juiste oplossing is maar stel bijvoorbeeld dat je een opensource product hebt waarin je dingen wilt aanpassen. Je hebt dan wel meteen een probleem als er een upgrade uitkomt als je in de core hebt lopen hacken, mogelijk krijg je dan conflicten. Wat je zou kunnen doen is classes met aanpassingen (als het een OOP systeem is) in een aparte directory zetten die eerst geinspecteerd wordt. Zo kun je dus code overriden zonder de core-bestanden aan te passen.
Een super simpele autoloader ziet er bijvoorbeeld als volgt uit:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// shorthands
define('__DS', DIRECTORY_SEPARATOR);
define('__PS', PATH_SEPARATOR);
// stel volgorde van laden in - eerst custom, dan core, dan de rest
set_include_path(MY_CUSTOM_PATH.__PS.MY_CORE_PATH.__PS.get_include_path());
// autoloader
function my_autoloader($class) {
spl_autoload(strtr($class, '_', __DS));
}
// vanaf PHP 5.3.0 kun je ook een anonieme functie gebruiken
spl_autoload_register('my_autoloader');
?>
// shorthands
define('__DS', DIRECTORY_SEPARATOR);
define('__PS', PATH_SEPARATOR);
// stel volgorde van laden in - eerst custom, dan core, dan de rest
set_include_path(MY_CUSTOM_PATH.__PS.MY_CORE_PATH.__PS.get_include_path());
// autoloader
function my_autoloader($class) {
spl_autoload(strtr($class, '_', __DS));
}
// vanaf PHP 5.3.0 kun je ook een anonieme functie gebruiken
spl_autoload_register('my_autoloader');
?>
Bovenstaand voorbeeld werkt overigens met underscores, niet met namespaces.
Maar goed, als je autoloader te lang wordt en/of je allerlei disk operatie achtige functies aan het uitvoeren bent is dit een redelijk indicatie dat je verkeerd bezig bent :). Autoloaders zouden simpel moeten zijn.
@Frank bedankt voor de link, als ik tijd heb zal ik het eens aandachtig doorlezen.
Zie alleen nu al dat mijn file structuur totaal niet zo is opgebouwd. Dus ik heb nog wat te doen denk ik zo.
Wat ik nu heb is zo:
components
- com_page
-- controller.php
-- model.php
-- view.php
-- output.php
layout
- layout.php
- libraries
-- paf
--- class.databaseconnection.php
--- class.error.php
--- class.layout.php
config.php
index.php
@Thomas oke, het is me nu veel duidelijker hoe een autloader zou moeten werken. Nu is het in dit geval niet van belang dat bepaalde bestand overschreven kunnen worden, meer dat er eerst gezocht wordt in de libraries map en daarna in de components map. Zou in wezen hetzelfde principe moeten zijn toch?
Over je code had ik ook nog een vraagje, werkt spl_autoload() in weze hetzelfde als een include_once of iets dergelijks?
spl_autoload() "include" zelf niets, maar als je ergens in je code een object van een klasse X aanmaakt dan worden je geregistreerde functies doorlopen en daarmee wordt getracht om met behulp van je "mapping" het correcte bestand te vinden (als ik het allemaal goed begrepen heb :)).
Je klasses worden hiermee ook pas "geinclude" als je ze gebruikt, je include dus precies wat je gebruikt. Niet teveel, en niet te weinig.
Niels van der Veer op 01/05/2015 13:41:56:
Zie alleen nu al dat mijn file structuur totaal niet zo is opgebouwd. Dus ik heb nog wat te doen denk ik zo.
Met namespaces en autoloading is eigenlijk elke mappenstructuur mogelijk.
Jouw controller class zit in de components map.
Je namespace kan dan Components zijn.
Stel dat je controller class gebruik wil maken van de model class dan krijg je zoiets:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
<?php
namespace Components;
use Components\Model;
class Controller {
function __construct() {
$this->model = new Model();
}
}
?>
namespace Components;
use Components\Model;
class Controller {
function __construct() {
$this->model = new Model();
}
}
?>
Persoonlijk zou ik je wel willen aanraden om:
- class namen met een hoofdletter te laten beginnen
- de .php bestanden dezelfde naam te geven als de classes, inclusief de hoofdletter
Bijv:
- BaseController
- Validator
- EmailValidator
etc
variabelen begin je altijd met een kleine letter.
Bijv:
- $controller
- $validator
- $emailValidator
Na wat puzzelen en proberen ben ik op het volgende uitgekomen. Is dat een beetje wat het idee is?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
// shorthands
define('__DS', DIRECTORY_SEPARATOR);
define('__PS', PATH_SEPARATOR);
define('APPLICATION_ROOT', __DIR__);
// stel volgorde van laden in - eerst custom, dan core, dan de rest
set_include_path(APPLICATION_ROOT . "\components" . __DS . __PS . APPLICATION_ROOT . "\libraries\paf" . __DS);
// autoloader
function my_autoloader($class) {
spl_autoload($class);
}
// vanaf PHP 5.3.0 kun je ook een anonieme functie gebruiken
spl_autoload_register('my_autoloader');
echo get_include_path();
$page = new Components\Page\PageModal::display();
echo $page->display();
?>
// shorthands
define('__DS', DIRECTORY_SEPARATOR);
define('__PS', PATH_SEPARATOR);
define('APPLICATION_ROOT', __DIR__);
// stel volgorde van laden in - eerst custom, dan core, dan de rest
set_include_path(APPLICATION_ROOT . "\components" . __DS . __PS . APPLICATION_ROOT . "\libraries\paf" . __DS);
// autoloader
function my_autoloader($class) {
spl_autoload($class);
}
// vanaf PHP 5.3.0 kun je ook een anonieme functie gebruiken
spl_autoload_register('my_autoloader');
echo get_include_path();
$page = new Components\Page\PageModal::display();
echo $page->display();
?>
Ik zie zelf alleen een nadeel hieraan.
Naar mijn idee is het nu niet helemaal zoals het zou moeten zijn, met mij vorige manier kon ik namelijk het volgende:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
Om dit nu te kunnen zou ik veel langere classes krijgen...
Of zie ik dat fout?
Gewijzigd op 01/05/2015 20:54:12 door Niels Veer
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
wordt dan dit:
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?php
namespace ....;
use Components\DatabaseConnection;
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
namespace ....;
use Components\DatabaseConnection;
$db = new DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
of:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
namespace ....;
$db = new \Components\DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
namespace ....;
$db = new \Components\DatabaseConnection;
$db->query("SELECT * FROM users");
$result = $db->excecute();
?>
even oppassen met standaard PHP classen trouwens:
$date = DateTime();
moet nu worden:
$date = \DateTime();