How to load controller / create router for php mvc framework - php

I am writing a light weight php mvc framework for my portfolio and as a barebone setup for my future developments. I am stuck now because I am trying to use PSR-4 autoloader through composer. I understand the concept of PSR-4 etc but I am wondering how should I handle the routing.
My folder structure is:
--|-Root---
index.php
composer.json
-----|-application------
--------|-controller----
--------|-model---------
--------|-core----------
-----------|-baseController.php
-----------|-Application.php
-----------|-baseView.php
-----------|-baseModel.php
--------|-view----------
--------|-config
-----------|-config.php
-----|-vendor------
--------|-autoload.php
-----|-assets------
I am fairly good with php but writing an own mvc framework is a hard challange, even bigger because I never used PSR standards before.
Now my questions are:
Should I use router at all?
If yes then what pattern should I use to acomplish that.
So, in my index.php I define all constants to store directories like ROOT, APP, VENDOR. I then load vendor/autoload.php generated from my composer.json. After autoload.php I load and initiate my config by calling this code:
if(is_readable(APP . 'config/config.php'))
{
require APP . 'config/config.php';
//Initiate config
new AppWorld\Confyy\config();
}
else
{
throw new Exception('config.php file was not found in' . APP . 'config');
}
After config I setup application environment and then I initiate my app by calling:
// Start the application
new AppWorld\FrostHeart\Application();
Then my baseController is very very thin:
<?php
namespace AppWorld\FrostHeart;
use AppWorld\FrostHeart\baseView as View;
abstract class baseController {
public $currentView;
public function __construct() {
//Initiate new View object and set currentView to this object.
$this->currentView = new View();
}
}
And this is my baseView.php:
<?php
namespace AppWorld\FrostHeart;
class baseView {
public function show($file, $data = null, $showTemplate = 1) {
if($data != null) {
foreach($data as $key => $value) {
$this->{$key} = $value;
}
}
if($showTemplate === 1) {
require VIEW_DIR . header . ".php";
require VIEW_DIR . $file . ".php";
require VIEW_DIR . footer . ".php";
}
elseif($showTemplate === 0)
{
require VIEW_DIR . $file . ".php";
}
}
}
my config.php:
<?php
namespace AppWorld\Confyy;
class config {
public function __construct() {
$this->application();
$this->database();
}
public function application() {
/**
* Define application environment, defaults are:
*
* development
* live
*
*/
define("ENVIRONMENT", "development");
//Define base url
define("BASE_URL", "http://" . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
//Define default controller
define("DEFAULT_CONTROLLER", "landing");
//Define default method
define("DEFAULT_METHOD", "index");
//Define controllers directory
define("CONTROLLER_DIR", APP . "controller/");
//Define views directory
define("VIEW_DIR", APP . "view/");
}
public function database() {
//Define DB Server
define("DB_SERVER", "localhost");
//Define DB User
define("DB_USER", "root");
//Define DB Password
define("DB_PASSWORD", "");
//Define DB Name
define("DB_NAME", "test");
}
}
and at last my composer.json autoload section:
"autoload": {
"psr-4":{
"AppWorld\\": "application",
"AppWorld\\Conffy\\": "application/config",
"AppWorld\\FrostHeart\\": "application/core",
"AppWorld\\Controls\\": "application/controller"
}
}
So how should I implement a router that routes the URL requests and load correct files?
Thanks!

Related

How to manage includes, require and use in this project

In this PHP project without framework, I have this folder structure: Adapter, Class and Models
A php file "index.php" is executed from the root and I have problems handling the model and adapter classes
Index file
<?php
include('Class/Load.php');
$connection = MysqlClass::getConnectionMysql();
Class Load
<?php
include(__DIR__ . DIRECTORY_SEPARATOR . 'MysqlClass.php');
include(__DIR__ . DIRECTORY_SEPARATOR . 'UtilsClass.php');
include(__DIR__ . DIRECTORY_SEPARATOR . 'EmailClass.php');
MysqlClass File
<?php
include ('UtilsClass.php');
class MysqlClass
{
/**
* #return PDO
*/
public static function getConnectionMysql(): PDO
{
$dbhost = ReadEnvFileClass::getConfig('MYSQL_LOCAL_HOST');
$dbuser = ReadEnvFileClass::getConfig('MYSQL_LOCAL_USER');
$dbpass = ReadEnvFileClass::getConfig('MYSQL_LOCAL_PWD');
$dbname = ReadEnvFileClass::getConfig('MYSQL_LOCAL_DBNAME');
try {
$dsn = "mysql:host=$dbhost;dbname=$dbname";
$dbh = new PDO($dsn, $dbuser, $dbpass);
} catch (PDOException $e){
var_dump($dbhost,$dbuser,$dbpass);
echo $e->getMessage();
}
return $dbh;
}
}
The question is in this second MysqlClass file if I should include here the files to the different classes that I need, or should I do it in the index.php file from a load.php file and from there load all the classes that I need in the rest of the project .
It is always a good idea to use an autoloader, like the one provided by Composer.
First, move Adapter, Class and Models subdirectories under a directory src. Remove Load.php completely.
The structure will be:
index.php
composer.json
src/Class/MysqlClass.php
src/Class/UtilsClass.php
src/Class/EmailClass.php
src/Adapter/...
src/Models/...
Then create the composer.json file in the main directory:
{
"autoload": {
"psr-4": {"Acme\\": "src/"}
}
}
In all class files, put the proper namespace and remove all include and require calls:
<?php
namespace Acme/Class
class MysqlClass {
// ...
Run composer install or just composer dump-autoload in the main directory, and include the autoload.php file in your index.php (remove all other includes and requires).
<?php
require __DIR__ . '/vendor/autoload.php';
Now you can call this code from any place, the class will be loaded if needed:
use Acme/Class/MysqlClass
// ...
$connection = MysqlClass::getConnectionMysql();

Is it possible to not use namespaces in some classies? I'm in trouble with autoload

I'm working with a old project. Classies have no namespacies.
Path structure:
-container // Container
--app
----model // all classies without namespace
----model_common // all classies used by other two projects without namespace
------Rede // new librarie with namespace
--------Exception
--------Service
----view
----controller
spl_autoload_register function is in a file named init_client.php, inside app path:
define('M_CMN', CLI_APP. 'model_common/'); // $_SERVER['DOCUMENT_ROOT'] . '/app/model_common/'
define('M_CLI', CLI_APP. 'model/'); // $_SERVER['DOCUMENT_ROOT'] . '/app/model/'
function autoload_client($class){
$path_and_class = str_replace('\\', DIRECTORY_SEPARATOR, $class); // May case there is a namespace
if (file_exists(M_CMN . "{$path_and_class}.php")): // First local to find class
require_once M_CMN . "{$path_and_class}.php";
elseif (file_exists(M_CLI . "{$path_and_class}.php")):
require_once M_CLI . "{$path_and_class}.php";
else :
ErrorFunction("Class {$path_and_class} was not found.",ERROR_1);
endif;
}
spl_autoload_register('autoload_client');
Example:
$consult = new DealConsult; // app/model
$consult->checkTransaction('123') // It will use Rede\Store, Rede\Environment and Rede\eRede classies
Error: Class Rede/Store was not found. But file $_SERVER['DOCUMENT_ROOT'] . '/app/model/Rede/Store.php' exist.
DealConsult.php class:
use Rede\Store;
use Rede\Environment;
use Rede\eRede;
class DealConsult {
public function checkTransaction($cod) {
$this->store = new Rede\Store($_SESSION['trans']['id_filiacao'], $_SESSION['trans']['token'], Rede\Environment::sandbox());
$this->transaction = (new Rede\eRede($this->store))->getByReference($cod);
printf("Autorization status: %s\n", $this->transaction->getAuthorization()->getStatus());
}
What am I not getting understand? I'm learning namespacies recently as well as PSR-4 defaults.
Use Composer to automatically load all the classes. In your composer.json file, you need to have:
{
"autoload": {
"psr-4": {
"Rede\\": "container/app/model_common/"
// Add as many classes as you need here in this map...
}
}
}
After that, run:
compose dump-autoload --optimize
Finally, include vendor/autoload.php where necessary.

Class not Found in PHP PDO FetchAll FETCH_CLASS [duplicate]

I'm working on a project whereby I have the following file structure:
index.php
|---lib
|--|lib|type|class_name.php
|--|lib|size|example_class.php
I'd like to auto load the classes, class_name and example_class (named the same as the PHP classes), so that in index.php the classes would already be instantiated so I could do:
$class_name->getPrivateParam('name');
I've had a look on the net but can't quite find the right answer - can anyone help me out?
EDIT
Thanks for the replies. Let me expand on my scenario. I'm trying to write a WordPress plugin that can be dropped into a project and additional functionality added by dropping a class into a folder 'functionality' for example, inside the plugin. There will never be 1000 classes, at a push maybe 10?
I could write a method to iterate through the folder structure of the 'lib' folder, including every class then assigning it to a variable (of the class name), but didn't think that was a very efficient way to do it but it perhaps seems that's the best way to achieve what I need?
Please, if you need to autoload classes - use the namespaces and class names conventions with SPL autoload, it will save your time for refactoring.
And of course, you will need to instantiate every class as an object.
Thank you.
Like in this thread:
PHP Autoloading in Namespaces
But if you want a complex workaround, please take a look at Symfony's autoload class:
https://github.com/symfony/ClassLoader/blob/master/ClassLoader.php
Or like this (I did it in one of my projects):
<?
spl_autoload_register(function($className)
{
$namespace=str_replace("\\","/",__NAMESPACE__);
$className=str_replace("\\","/",$className);
$class=CORE_PATH."/classes/".(empty($namespace)?"":$namespace."/")."{$className}.class.php";
include_once($class);
});
?>
and then you can instantiate your class like this:
<?
$example=new NS1\NS2\ExampleClass($exampleConstructParam);
?>
and this is your class (found in /NS1/NS2/ExampleClass.class.php):
<?
namespace NS1\NS2
{
class Symbols extends \DB\Table
{
public function __construct($param)
{
echo "hello!";
}
}
}
?>
If you have an access to the command line, you can try it with composer in the classMap section with something like this:
{
"autoload": {
"classmap": ["yourpath/", "anotherpath/"]
}
}
then you have a wordpress plugin to enable composer in the wordpress cli : http://wordpress.org/plugins/composer/
function __autoload($class_name) {
$class_name = strtolower($class_name);
$path = "{$class_name}.php";
if (file_exists($path)) {
require_once($path);
} else {
die("The file {$class_name}.php could not be found!");
}
}
UPDATE:
__autoload() is deprecated as of PHP 7.2
http://php.net/manual/de/function.spl-autoload-register.php
spl_autoload_register(function ($class) {
#require_once('lib/type/' . $class . '.php');
#require_once('lib/size/' . $class . '.php');
});
I have an example here that I use for autoloading and initiliazing.
Basically a better version of spl_autoload_register since it only tries to require the class file whenever you initializes the class.
Here it automatically gets every file inside your class folder, requires the files and initializes it. All you have to do, is name the class the same as the file.
index.php
<?php
require_once __DIR__ . '/app/autoload.php';
$loader = new Loader(false);
User::dump(['hello' => 'test']);
autoload.php
<?php
class Loader
{
public static $library;
protected static $classPath = __DIR__ . "/classes/";
protected static $interfacePath = __DIR__ . "/classes/interfaces/";
public function __construct($requireInterface = true)
{
if(!isset(static::$library)) {
// Get all files inside the class folder
foreach(array_map('basename', glob(static::$classPath . "*.php", GLOB_BRACE)) as $classExt) {
// Make sure the class is not already declared
if(!in_array($classExt, get_declared_classes())) {
// Get rid of php extension easily without pathinfo
$classNoExt = substr($classExt, 0, -4);
$file = static::$path . $classExt;
if($requireInterface) {
// Get interface file
$interface = static::$interfacePath . $classExt;
// Check if interface file exists
if(!file_exists($interface)) {
// Throw exception
die("Unable to load interface file: " . $interface);
}
// Require interface
require_once $interface;
//Check if interface is set
if(!interface_exists("Interface" . $classNoExt)) {
// Throw exception
die("Unable to find interface: " . $interface);
}
}
// Require class
require_once $file;
// Check if class file exists
if(class_exists($classNoExt)) {
// Set class // class.container.php
static::$library[$classNoExt] = new $classNoExt();
} else {
// Throw error
die("Unable to load class: " . $classNoExt);
}
}
}
}
}
/*public function get($class)
{
return (in_array($class, get_declared_classes()) ? static::$library[$class] : die("Class <b>{$class}</b> doesn't exist."));
}*/
}
You can easily manage with a bit of coding, to require classes in different folders too. Hopefully this can be of some use to you.
You can specify a namespaces-friendly autoloading using this autoloader.
<?php
spl_autoload_register(function($className) {
$file = __DIR__ . '\\' . $className . '.php';
$file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
if (file_exists($file)) {
include $file;
}
});
Make sure that you specify the class file's location corretly.
Source
spl_autoload_register(function ($class_name) {
$iterator = new DirectoryIterator(dirname(__FILE__));
$files = $iterator->getPath()."/classes/".$class_name.".class.php";
if (file_exists($files)) {
include($files);
} else {
die("Warning:The file {$files}.class.php could not be found!");
}
});
do this in a file and called it anything like (mr_load.php)
this were u put all your classes
spl_autoload_register(function($class){
$path = '\Applicaton/classes/';
$extension = '.php';
$fileName = $path.$class.$extension;
include $_SERVER['DOCUMENT_ROOT'].$fileName;
})
;
then create another file and include mr_load.php; $load_class = new BusStop(); $load_class->method()

Slim v3 Using Custom Class to add Middleware

I want to add Middleware to my Slim project that checks the ip of the user before allowing them access.
My middleware class:
<?php
namespace App\Middleware;
Class IpFilter
{
protected $request_ip;
protected $allowed_ip;
public function __construct($allowedip = array('127.0.0.1'))
{
$this->request_ip = app()->request()->getIp();
$this->allowed_ip = $allowedip;
}
public function call()
{
$checkit = checkIp();
$this->next->call();
}
protected function checkIp()
{
if (!in_array($this->request_ip, $this->allowed_ip))
$app->halt(403);
}
}
My Bootstrap index.php:
<?php
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) {
return false;
}
require __DIR__ . '/../vendor/autoload.php';
require '../app/middleware/ipfilter.php';
// Instantiate the app
$settings = require __DIR__ . '/../app/settings.php';
$app = new \Slim\App($settings);
$app->get('/test', function() {
echo "You look like you're from around here";
});
// Set up dependencies
require __DIR__ . '/../app/dependencies.php';
// Register middleware
require __DIR__ . '/../app/middleware.php';
// Register routes
require __DIR__ . '/../app/routes.php';
$app->add(new IpFilter);
// Run
$app->run();
I am using a slim skeleton project for my project setup. I get the following error when I run this code.
Fatal error: Class 'IpFilter' not found in
/Applications/XAMPP/xamppfiles/htdocs/slimtest/my-app/public/index.php
on line 34
I still don't properly understand how to add custom classes for middleware in slim. I've seen several tutorials that just make the class and use $app->add('new class) to add the middleware but I can't figure it out. Is there a file I need to update and I am just missing it?
It's been a long weekend with slim and not a lot of resources out there so any help would be greatly appreciated.
UPDATE:
When I remove the namespace App\Middleware from ipfilter.php I don't get the same error. This time I get
Fatal error: Call to undefined method IpFilter::request() in /Applications/XAMPP/xamppfiles/htdocs/slimtest/my-app/app/middleware/ipfilter.php on line 15
Which I understand why but I thought it might help troubleshoot and get to the root of the problem.
Okay, Finally got it to work.
Index.php
<?php
// To help the built-in PHP dev server, check if the request was actually for
// something which should probably be served as a static file
if (PHP_SAPI === 'cli-server' && $_SERVER['SCRIPT_FILENAME'] !== __FILE__) {
return false;
}
use App\Middleware\IpFilter;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../app/middleware/ipfilter.php';
// Instantiate the app
$settings = require __DIR__ . '/../app/settings.php';
$app = new \Slim\App($settings);
$app->get('/test', function() {
echo "You look like you're from around here";
});
// Set up dependencies
require __DIR__ . '/../app/dependencies.php';
// Register middleware
require __DIR__ . '/../app/middleware.php';
// Register routes
require __DIR__ . '/../app/routes.php';
$app->add(new IpFilter);
// Run
$app->run();
ipfilter.php
<?php
namespace App\Middleware;
Class IpFilter
{
private $whitelist = arrray('127.0.0.1')
protected $request_ip;
public function __invoke($request, $response, $next)
{
$request_ip = $request->getAttribute('ip_address');
return $next($request, $response);
}
public function call()
{
$checkit = checkIp();
$this->next->call();
}
protected function checkIp()
{
if (!in_array($this->request_ip, $this->whitelist)
$app->halt(403);
}
}
KEY: Using App\Middleware\Ipfilter in the index.php. I though using require to add the class would be enough but apparently no.
Shout out to codecourse.com, really helped.

Best Way To Autoload Classes In PHP

I'm working on a project whereby I have the following file structure:
index.php
|---lib
|--|lib|type|class_name.php
|--|lib|size|example_class.php
I'd like to auto load the classes, class_name and example_class (named the same as the PHP classes), so that in index.php the classes would already be instantiated so I could do:
$class_name->getPrivateParam('name');
I've had a look on the net but can't quite find the right answer - can anyone help me out?
EDIT
Thanks for the replies. Let me expand on my scenario. I'm trying to write a WordPress plugin that can be dropped into a project and additional functionality added by dropping a class into a folder 'functionality' for example, inside the plugin. There will never be 1000 classes, at a push maybe 10?
I could write a method to iterate through the folder structure of the 'lib' folder, including every class then assigning it to a variable (of the class name), but didn't think that was a very efficient way to do it but it perhaps seems that's the best way to achieve what I need?
Please, if you need to autoload classes - use the namespaces and class names conventions with SPL autoload, it will save your time for refactoring.
And of course, you will need to instantiate every class as an object.
Thank you.
Like in this thread:
PHP Autoloading in Namespaces
But if you want a complex workaround, please take a look at Symfony's autoload class:
https://github.com/symfony/ClassLoader/blob/master/ClassLoader.php
Or like this (I did it in one of my projects):
<?
spl_autoload_register(function($className)
{
$namespace=str_replace("\\","/",__NAMESPACE__);
$className=str_replace("\\","/",$className);
$class=CORE_PATH."/classes/".(empty($namespace)?"":$namespace."/")."{$className}.class.php";
include_once($class);
});
?>
and then you can instantiate your class like this:
<?
$example=new NS1\NS2\ExampleClass($exampleConstructParam);
?>
and this is your class (found in /NS1/NS2/ExampleClass.class.php):
<?
namespace NS1\NS2
{
class Symbols extends \DB\Table
{
public function __construct($param)
{
echo "hello!";
}
}
}
?>
If you have an access to the command line, you can try it with composer in the classMap section with something like this:
{
"autoload": {
"classmap": ["yourpath/", "anotherpath/"]
}
}
then you have a wordpress plugin to enable composer in the wordpress cli : http://wordpress.org/plugins/composer/
function __autoload($class_name) {
$class_name = strtolower($class_name);
$path = "{$class_name}.php";
if (file_exists($path)) {
require_once($path);
} else {
die("The file {$class_name}.php could not be found!");
}
}
UPDATE:
__autoload() is deprecated as of PHP 7.2
http://php.net/manual/de/function.spl-autoload-register.php
spl_autoload_register(function ($class) {
#require_once('lib/type/' . $class . '.php');
#require_once('lib/size/' . $class . '.php');
});
I have an example here that I use for autoloading and initiliazing.
Basically a better version of spl_autoload_register since it only tries to require the class file whenever you initializes the class.
Here it automatically gets every file inside your class folder, requires the files and initializes it. All you have to do, is name the class the same as the file.
index.php
<?php
require_once __DIR__ . '/app/autoload.php';
$loader = new Loader(false);
User::dump(['hello' => 'test']);
autoload.php
<?php
class Loader
{
public static $library;
protected static $classPath = __DIR__ . "/classes/";
protected static $interfacePath = __DIR__ . "/classes/interfaces/";
public function __construct($requireInterface = true)
{
if(!isset(static::$library)) {
// Get all files inside the class folder
foreach(array_map('basename', glob(static::$classPath . "*.php", GLOB_BRACE)) as $classExt) {
// Make sure the class is not already declared
if(!in_array($classExt, get_declared_classes())) {
// Get rid of php extension easily without pathinfo
$classNoExt = substr($classExt, 0, -4);
$file = static::$path . $classExt;
if($requireInterface) {
// Get interface file
$interface = static::$interfacePath . $classExt;
// Check if interface file exists
if(!file_exists($interface)) {
// Throw exception
die("Unable to load interface file: " . $interface);
}
// Require interface
require_once $interface;
//Check if interface is set
if(!interface_exists("Interface" . $classNoExt)) {
// Throw exception
die("Unable to find interface: " . $interface);
}
}
// Require class
require_once $file;
// Check if class file exists
if(class_exists($classNoExt)) {
// Set class // class.container.php
static::$library[$classNoExt] = new $classNoExt();
} else {
// Throw error
die("Unable to load class: " . $classNoExt);
}
}
}
}
}
/*public function get($class)
{
return (in_array($class, get_declared_classes()) ? static::$library[$class] : die("Class <b>{$class}</b> doesn't exist."));
}*/
}
You can easily manage with a bit of coding, to require classes in different folders too. Hopefully this can be of some use to you.
You can specify a namespaces-friendly autoloading using this autoloader.
<?php
spl_autoload_register(function($className) {
$file = __DIR__ . '\\' . $className . '.php';
$file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
if (file_exists($file)) {
include $file;
}
});
Make sure that you specify the class file's location corretly.
Source
spl_autoload_register(function ($class_name) {
$iterator = new DirectoryIterator(dirname(__FILE__));
$files = $iterator->getPath()."/classes/".$class_name.".class.php";
if (file_exists($files)) {
include($files);
} else {
die("Warning:The file {$files}.class.php could not be found!");
}
});
do this in a file and called it anything like (mr_load.php)
this were u put all your classes
spl_autoload_register(function($class){
$path = '\Applicaton/classes/';
$extension = '.php';
$fileName = $path.$class.$extension;
include $_SERVER['DOCUMENT_ROOT'].$fileName;
})
;
then create another file and include mr_load.php; $load_class = new BusStop(); $load_class->method()

Categories