I try to build test project with PatrickLouys/no-framework-tutorial
My Controllers/BaseController.php:
<?php
namespace Example\Controllers;
class BaseController
{
public function test(){
echo 'test';
}
}
And index.php file:
<?php
namespace Example;
require __DIR__.'/vendor/autoload.php';
// come code with routes
case \FastRoute\Dispatcher::FOUND:
$className = $routeInfo[1][0];
$method = $routeInfo[1][1];
$vars = $routeInfo[2];
$class = new $className; <-- load the class specified in the routes
$class->$method($vars);
break;
// some code
When I tried run this code I got error:
Class 'Example\Controllers\BaseController' not found
Can you please tell me where I made a mistake?
Related
I have an autoloader that is placed as a php file above all other sub directories in my project.
What it does is it loads all possible classes at once for any specific server request. After further thought I concluded I need to autoload only the required classes.
What do I need to do to avoid loading other classes not needed?
If I need to post the relevant code snippets of the class files in my subdirectories, I can.
<?php
namespace autoloader;
class autoloader
{
private $directoryName;
public function __construct($directoryName)
{
$this->directoryName = $directoryName;
}
public function autoload()
{
foreach (glob("{$this->directoryName}/*.class.php") as $filename)
{
include_once $filename;
}
foreach (glob("{$this->directoryName}/*.php") as $filename)
{
include_once $filename;
}
}
}
# nullify any existing autoloads
spl_autoload_register(null, false);
# instantiate the autoloader object
$classes = [
new autoloader('request'),
new autoloader('config'),
new autoloader('controllers'),
new autoloader('models'),
new autoloader('data')
];
# register the loader functions
foreach ($classes as $class)
spl_autoload_register(array($class, 'autoload'));
All registered autoloader functions will be called when you try to instantiate a new class or until it finally loads the class or throws an error. The way you have it now, you're registering the same autoloader function again and again for each directory, and file.
What you'd want to do is something along the lines of this.
namespace autoloader;
class autoloader
{
public function __construct()
{
spl_autoload_register([$this, 'autoload']);
}
public function autoload($classname)
{
if (! file_exists("{$classname}.class.php")) {
return;
}
include_once "{$classname}.class.php";
}
}
new autoloader();
Every autoloader function gets the class FQCN passed into it, and from there you'll have to parse it and figure out if you can load the file where that class exists. For instance, if I do the following.
use Some\Awesome\ClassFile;
$class = new ClassFile();
The autoloader we've registered will get the string Some\Awesome\ClassFile passed in as an argument, which we can then parse and see if we have a file for that class, if we don't we return out of the function and let the next registered autoloader function try and find the class.
You can read more about autoloaders in the documentation, I also wrote a blog post about it like 2 months ago that might interest you.
I had to refactor the code and remove the unnecessary load all functionality that I mistakenly thought would lazy load my classes on request.
Here is what I came up with:
Entry Point
<?php
require_once 'enums.php';
require_once 'api.class.php';
spl_autoload('AutoLoader\AutoLoader');
use App\API;
class MyAPI extends API
{
public function __construct($request){
parent::__construct($request);
}
}
$api = new MyAPI($_REQUEST);
echo $api->processRequest();
AutoLoader Implementation (located under subdirectory Autoloader/Autoloader.php and inaccessible via browser by using .htaccess)
<?php
namespace Autoloader;
spl_autoload_register("AutoLoader\AutoLoader::ClassLoader");
spl_autoload_register("AutoLoader\AutoLoader::RequestLoader");
class Autoloader
{
public static function ClassLoader(String $fileName)
{
foreach ([".Class.php", ".php"] as $extension)
if (file_exists($fileName.$extension))
include $fileName.$extension;
}
public static function RequestLoader()
{
self::ClassLoader('Request');
}
}
Snippet for processRequest() (located in api.class.php - my request router)
public function processRequest()
{
$id1 = $this->requestObj->id1;
$id2 = $this->requestObj->id2;
$endpoint1 = $this->requestObj->endpoint1;
$endpoint2 = $this->requestObj->endpoint2;
$goto = $this->requestObj->goto;
$isDestination = in_array($id1, ['first', 'prev', 'next', 'last']);
$numSetEndpoints = (int)isset($endpoint1) + (int)isset($endpoint2);
switch($numSetEndpoints)
{
case 0:
if ($isDestination)
return json_decode($this->_response("No Endpoint: ", $endpoint1));
return json_decode($this->_response("No Endpoint: " . $endpoint2 ?? $endpoint));
case 1:
$className = $endpoint1.'Controller';
break;
case 2:
$className = $endpoint2.'Controller';
break;
}
$class = "\\Controllers\\$className";
if (class_exists($class))
{
$method = strtolower($this->method);
if (method_exists($class, $method))
{
$response = (new $class($this->requestObj))->{$method}();
if ($response['Succeeded'] == false)
{
return $response['Result'];
}
else if ($response['Succeeded'] == true)
{
header("Content-Type: application/json");
return $this->_response($response);
}
else if ($response['Result'])
{
header("Content-Type: text/html");
return $this->_response($response);
}
}
}
}
I am building custom mvc framework from scratch and when I put something something to print in my PageController, application is working, but when I create some other controller and try to print something, it just prints data from PageController.
I think that the problem is on where I put variable protected $currentController = 'PageController'; and $this->currentController = new PageController();.
Now I tried putting $this->currentController = new $this->currentController; and I get this error:
'Fatal error: Uncaught Error: Class 'PageController' not found in C:\xampp\htdocs\php\App\Libraries\Core.php:20 Stack trace: #0 C:\xampp\htdocs\php\public\index.php(7): App\Libraries\Core->__construct() #1 {main} thrown in C:\xampp\htdocs\php\App\Libraries\Core.php on line 20 '.
Also I'm using composer and autoloading with psr-4 and namespacing.
Core.php
namespace App\Libraries;
use App\Controllers\PageController;
class Core
{
protected $currentController = 'PageController';
protected $currentMethod = 'index';
protected $params = [];
public function __construct()
{
$url = $this->getUrl();
if (file_exists('../App/Controllers/' . ucwords($url[0]) . '.php')) {
$this->currentController = ucwords($url[0]);
unset($url[0]);
}
require_once '../App/Controllers/' . $this->currentController . '.php';
$this->currentController = new PageController();
}
public function getUrl()
{
if (isset($_GET['url'])) {
$url = rtrim($_GET['url'], '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
return $url;
}
}
}
PageController.php
namespace App\Controllers;
class PageController
{
public function __construct()
{
echo "Page Loaded!!!";
}
}
PostController.php
namespace App\Controllers;
class PostController
{
public function __construct()
{
echo "Post Controller Loaded!!!";
}
}
index.php (in public folder)
use App\Libraries\Core;
require_once '../App/bootstrap.php';
$init = new Core;
bootstrap.php (in App folder)
<?php
require dirname(__DIR__) . '..\vendor\autoload.php';
I expect when I make some other controller to be able to write something in it and get output without errors or the same data as in PageController.
I pulled your repository down and there were a few things I had to change.
Make sure that your RewriteBase in your .htaccess goes from the correct document root. If you're not using virtual hosts, then you need to use the path from the localhost to the project.
Also, you are still using a require statement before calling the object. You do not need that. Remove it.
At the top, add the namespace to the $currentController property. If $url[0] is set, prefix the namespace like this:
$this->currentController = '\App\Controllers\\' . ucwords($url[0]);
That got the posts controller and page controllers working.
I have Service directory in my symfony application like below
src/SwipeBundle/Service
Inside of this directory I have class called.
RSA.php
This class has
namespace SwipeBundle\Service;
require_once __DIR__ . 'RSA/Crypt/RSA.php';
require_once __DIR__ . 'RSA/File/X509.php';
class RSA
{
public function privateKey() {
require_once __DIR__ . 'RSA/Certificates/private.txt';
}
public function certificates() {
return require_once __DIR__ . 'RSA/Certificates/pinew.cer';
}
}
and the files directory are
src/SwipeBundle/Service/RSA/Certificates/pinew.cer
src/SwipeBundle/Service/RSA/Certificates/private.txt
src/SwipeBundle/Service/RSA/Crypt/RSA.php
src/SwipeBundle/Service/RSA/File/X509.php
I want to load the classes of this service class to my Controller, like so.
Controller
use SwipeBundle\Service;
class BillsPayController extends Controller
{
public function indexAction() {
$rsa = new \Crypt_RSA();
$hash = new \Crypt_Hash('sha1');
$x509 = new \File_X509();
$privatekey = file_get_contents(RSA()->privateKey());
$x509->loadX509(file_get_contents(RSA()->certificates()));
}
}
I tried also using this one.
use SwipeBundle\Service\RSA;
class BillsPayController extends Controller
{
public function indexAction() {
$a = new RSA();
$a->privateKey();
}
}
Error I encountered.
Attempt result 1: Attempted to load class "Crypt_RSA" from the global namespace.
Did you forget a "use" statement?
Attempt result 2: Compile Error: main(): Failed opening required '/Users/jaysonlacson/Sites/contactless/src/SwipeBundle/ServiceRSA/Crypt/RSA.php' (include_path='.:')
I think you are missing a forward slash like this:
require_once __DIR__ . '/RSA/Crypt/RSA.php';
require_once __DIR__ . '/RSA/File/X509.php';
I'm just guessing, but can you try that?
My question is how to dynamically include and call the model and view classes in a simple MVC model? I have no problem calling the controller, I have found various ways to do that, but I can't find a good solution for calling and passing in the model and view.
I have setup a .htaccess file to read the url as "www.domain.com/controller/method/id".
I was previously trying to do a check if a file exists for the model and view the same way I am doing the controller using the $cont variable, and then trying to load the model and pass it into the controller, then the view. The issue I had is that all the includes are using the $cont variable to call instantiate their classes and could not tell each other apart. I tried adding a suffic $cont . 'Controller', but then I couldn't load the class at all, let alone pass in the model or view.
Here is my latest example without model or view.
<?php
//===============================================
// Debug
//===============================================
ini_set('display_errors','On');
error_reporting(E_ALL);
//===============================================
// Includes
//===============================================
require('coremvc.php');
//===============================================
// Constants & Globals
//===============================================
define('BASE_PATH', dirname(realpath(__FILE__)));
$GLOBALS['var'] = "";
//===============================================
// Session
//===============================================
session_start();
//===============================================
// Router
//===============================================
if ($_SERVER['REQUEST_URI'] !== '/') {
$uri = $_SERVER['REQUEST_URI'];
$uri = ltrim($uri, '/');
$request = explode('/', $uri);
foreach ($request as $key => $val) {
if(empty($request[$key])) {
unset($request[$key]);
}
}
$request = array_values($request);
if (isset($request[0])) {
$cont = $request[0];
}
if (isset($request[1])) {
$action = $request[1];
}
} else {
$cont = "home";
}
if (FILE_EXISTS('/controllers/' . $cont . 'Controller.php')) {
require '/controllers/' . $cont . 'Controller.php';
} else {
$cont = "home";
require '/controllers/homeController.php';
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont;
I have made the following changes to the example above, posted it below, as well as my super easy bake oven simple controller.
<?php
if (FILE_EXISTS('/controllers/' . $cont . 'Controller.php')) {
require '/controllers/' . $cont . 'Controller.php';
} else {
$cont = "home";
$cont = ucfirst($cont) . 'Controller';
require '/controllers/homeController.php';
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont('World');
$controller->world();
Controller, it is just extending an empty class which I was thinking I could use if I wanted to extend a common method to every class. That is what the coremvc.php is in the index.php above.
<?php
Class HomeController extends Controller
{
function __construct($world) {
echo "Hello ";
$this->world = $world;
}
function world() {
echo $this->world;
}
}
You want to load and call classes easily. I dynamically load classes that end in ".class.php". This makes things easy for me.
I put this in my index.php... where /app/ is where I have my php classes:
<?php
define('CLASS_DIR', '/app/');
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . CLASS_DIR);
spl_autoload_extensions('.class.php');
spl_autoload_register();
Next, I require my routes:
require 'rte/myroute.php';
I let my routes (controllers) direct my traffic to my models, albeit some parsing, etc.
I typically develop REST based APIs in PHP, so the "view" is just a JSON response.
The HTML / JavaScript client consumes the responses.
A good framework that I like is SlimPHP. I load it up using Composer.
http://www.slimframework.com/
https://getcomposer.org/
Here's an example of calling a class as an instance and statically, since you auto-loaded, you don't need to include anything at the top:
<?php
$param1 = 1;
$param2 = 2;
MyClass::myFunc($param1);
$myObj = new MyClass;
$myObj->myFunc2($param2);
As it is already say you can have a look to use the spl_autoload_register which will require all your files in the given path. You can change this function to improve the load.
Concerning the Model with your current code you can implement it as follow:
$controllerPath = "/controllers/{$cont}Controller.php";
$modelPath = "/model/{$cont}Model.php";
if (FILE_EXISTS($controllerPath)) {
require $controllerPath;
if (FILE_EXISTS($modelPath)) {
require $modelPath;
}
else {
throw new \LogicException(
sprintf("Your controller must implement a model. No model found: %s", $modelPath)
);
}
} else {
$cont = "home";
require "/controllers/{$cont}Controller.php";
require "/model/{$cont}Model.php";
}
//===============================================
// Start the controller
//===============================================
$controller = new $cont($cont);
In this sample of code, $cont is the name of the page like home. Which require the homeController and homeModel. Then in your __construct($modelName) just set the model.
However, I don't recommand you tu use this because your controller can load many Models. Then, your controller could look like this:
<?php
namespace \App\Controller; // If you use namespace
use App\Model\homeModel, // If you use namespace
App\Model\productModel; // If you use namespace
Class HomeController extends Controller
{
private $model;
/* If this function is common to all controllers just move it
in the parent Controller class( one more time, I don't recommend to set
the model using this way). */
public function __construct($model) {
$this->model= $model;
}
public function login() {
$homeModel = new homeModel(); // If you use namespace
// Example to call the view
$myValue = 'Hello world';
$this->render(array("myVariableName" => $myValue))
}
}
In this second example $this->render can be a method in your Controller class (which by the way should be an abstract class). I'll give a last sample of code for the logic of the abstract controller.
<?php
namespace \App\Controller;
abstract class AbstractController {
/* Common code for all controllers */
public function __construct() {
}
/* View renderer */
protected function render($parameters) {
/* Call the view here maybe an another class which manage the View*/
}
}
To conclude, you can implement this MVC in many way, this code is just a suggestion and maybe its not the best way. I advise you to have a look with the spl_autoload that I put at the beginning of my answer.
using same namespace php
I have this files in the same folder :
OtherFunctions.php
<?php
namespace Pack\sp;
$Tble = NULL;
function SetTble($tble) {
global $Tble;
$Tble = $tble;
}
function GetTble() {
global $Tble;
return $Tble;
}
function Funct0($Str0, $Str1) {
return $Str0 == $Str1;
}
function Funct1($Arg) {
return "The Value is ".$Arg;
}
//... from 0 to 16
function Funct16($Arg) {
return "The Value is ".$Arg;
}
?>
How to call all functions contained in this file?
In one class File SubClass.php I have this:
<?php
namespace Pack\sp;
class SubClass {
public $CArg = "";
}
?>
In other class File LeadClass.php
I have this:
<?php
namespace Pack\sp;
use \Pack\sp\SubClass;
require_once("OtherFunctions.php");
class LeadClass {
public function __construct($Name) {
echo("_._");
$NewSC = new SubClass();
$NewSC->CArg = $Name;
SetTble($Name);
echo("ini:".GetTble().":end");
}
}
?>
I want call all function in one instruction of OtherFunctions.php File, but I don't kno how to do it....
I trying to replicate this message in other code
Fatal error: Call to undefined function GetTble() in C:...\LeadClass.php on line 10
But, I'm obtaining blank page
EDIT
Was added the line:
require_once("OtherFunctions.php");
And was replaced the line:
require_once("SubClass.php");
by the line:
use \Pack\sp\SubClass;
in LeadClass.php File.
But, I'm obtaining blank page
You need to add the next line
namespace Pack\sp;
use \Pack\sp\SubClass; // <--- add this
Also I think you should put the functios of the OtherFunctions file into a new class link
namespace Pack\sp;
class OtherFunctions{
// your current code goes here
}
After that you need to extend the SubClass whit the OtherFunctios class
namespace Pack\sp;
use Pack\sp\OtherFunctions;
class SubClass extends OtherFunctions {
public $CArg = "";
}
EDIT
I just tried your code and I can make the LeasClass to work as follow
<?php
namespace Pack\sp;
require_once("OtherFunctions.php");
require_once("SubClass.php");
class LeadClass {
public function __construct($Name) {
echo("_._");
$NewSC = new SubClass();
$NewSC->CArg = $Name;
SetTble($Name);
echo("ini:".GetTble().":end");
}
}
$LeadClass = new LeadClass('table');
?>
Have you already initialize the class?