PHP MVC - Why is my request/router always going to home controller? - php

I've tried many fixes but for some reason my request and routing mechanisms in my framework aren't working properly. It's a multi-language site (en/fr) and I use mod_rewrite to get the language, class, method and an optional argument. My request.php sets the controller to languageController if the language isn't set, and it works (successfully echo "language" in controller). But, if the language is set, the controller that always ends up displaying is homeController despite that the uri is /en/about. I have no idea why this is happening and it's really frustrating me. Here's the problem code, help is appreciated.
Note: I haven't used namespaces as it is a small website that I just want to get up and running and there's no real need, although I plan to edit it to use namespaces in the future simply for the leaning.
relevant parts of index.php
//analyze request
$request = new Request($_GET); //breaks up the uri into an array
//routing
$router = new Router($request); //stores the requested uri in the router
$router->route(); //sets the proper route
//instancialize and execute the controller
$class = $router->getController();
$method = $router->getMethod();
$controller = new $class;
$controller->$method();
Request.php
class Request {
private $request;
public function __construct($uri) {
$this->request = $uri;
//if the language isn't selected
if($this->request['language']=="") {
$this->request['language'] = "language";
$this->request['class'] = "language";
$this->request['method'] = "index";
}
//else if the controller is not selected
else if($this->request['controller']=="") {
$this->request['class'] = "home";
$this->request['method'] = "index";
}
//stores the requested method
else if($this->request['method']=="") {
$this->request['method'] = "index";
}
//stores a value of false if there is no argument in the uri
if($this->request['arg']=="") {
$this->request['arg'] = false;
}
}
public function getLanguage() {
return $this->request['language'];
}
public function getClass() {
return $this->request['class'];
}
public function getMethod() {
return $this->request['method'];
}
public function getArgument() {
return $this->request['arg'];
}
}
Router.php
class Router {
private $language;
private $controller;
private $view;
private $method;
private $arg;
public function __construct(Request $request) { //given an instance of request, store it in local variables
$this->language = $request->getLanguage();
$this->controller = $request->getClass() . 'Controller';
$this->view = $request->getClass() . 'View';
$this->method = $request->getMethod();
$this->arg = $request->getArgument();
}
public function route() { //put the requested uri into forms we can deal with
if($this->language == "fr") { //compare it against url translation chart
//run all uri values through a chart that translates them from french to english if it finds a match
}
else { //it must be english
}
}
public function getLanguage() {
return $this->language;
}
public function getController() {
return $this->controller;
}
public function getView() {
return $this->view;
}
public function getMethod() {
return $this->method;
}
public function getArgument() {
return $this->arg;
}
}

Related

PHP: ReflectionMethod::getAttributes throwing error 'Call to undefined method'

I am very new to PHP (did some small projects a few years back, but nothing since) and trying to get familiar with it again by creating a RESTful API project. Coming from a C# background, I'm trying to setup the boiler plate stuff to be similar to that of .NET, where controllers handle the request and attributes help determine which function should handle the request.
The problem I'm running into is retrieving the attributes from a method within the Controller class. I create an instance of ReflectionClass, call getMethods() then attempt to iterate the methods to retrieve the attributes by calling $method->getAttributes(HttpGet::class) but the $method->getAttributes() call is throwing the following error
'PHP message: PHP Fatal error: Uncaught Error: Call to undefined method ReflectionMethod::getAttributes() in /var/www/*********/api/base/ApiController.php:36\
Here is the code
-Running on Apache2 / Linux with PHP 7.4.28
The Controller
require './base/ApiController.php';
require './base/attributes/RouteAttributes.php';
class SessionController extends ApiController
{
#[HttpGet('')]
public function OnGet()
{
parent::OK("GET Called - Returned 200 OK");
}
#[HttpPost('')]
public function OnPost()
{
parent::Created("POST called - Returned 201 CREATED");
}
}
$controller = new SessionController();
ApiController::HandleRequest($controller);
?>
The ApiController base class
/* Base class for all API controllers, provides basic functionality like
* returning common statuses, conversion of data to JSON and formatting
* appropriate headers
*/
require "HttpStatusCode.php";
class ApiController
{
//Return Types
public static function StatusCode(int $status, $data)
{
header('X-PHP-Response-Code: $status->value', true, $status);
if(isset($data))
{
header("Content-type: application/json");
echo json_encode($data);
}
exit;
}
public static function HandleRequest($controller)
{
$routePath = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : NULL;
$requestMethod = strtoupper($_SERVER['REQUEST_METHOD']);
$controllerInfo = new ReflectionClass($controller);
$methodFound = false;
switch($requestMethod)
{
case 'GET':
foreach($controllerInfo->getMethods() as $method)
{
echo $method;
$getAttribute = $method->getAttributes(HttpGet::class);
if(count($getAttribute))
{
$method.invoke($controller, NULL);
$methodFound = true;
}
}
break;
case 'POST':
break;
}
if($methodFound == false)
ApiController::StatusCode(HttpStatusCode::NotFound, "Method Not Found");
}
public function OK($data)
{
ApiController::StatusCode(HttpStatusCode::OK, $data);
}
public function Created($data, $createdAtRoute)
{
if(isset($createdAtRoute))
header('Location: $createdAtRoute');
StatusCode(HttpStatusCode::Created, $data);
}
public function Accepted($data)
{
StatusCode(HttpStatusCode::Accepted, $data);
}
public function NoContent()
{
StatusCode(HttpStatusCode::NoContent, NULL);
}
public function BadRequest($data)
{
StatusCode(HttpStatusCode::BadRequest, $data);
}
public function Unathorized($data)
{
StatusCode(HttpStatusCode::Unathorized, $data);
}
public function Forbidden($data)
{
StatusCode(HttpStatusCode::Forbidden, $data);
}
}
?>
The Attribute definitions
#[Attribute]
class HttpGet
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPost
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPut
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpPatch
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
#[Attribute]
class HttpDelete
{
public string $Route;
public function __construct(string $route)
{
$this->Route = $route;
}
}
?>
The project is very much still in the infancy stages and going to be a functional 'learner' project to get my feet wet. I just haven't been able to get past this error. Any help or insight would be greatly appreciated.
Your issue is with the PHP version. PHP Annotations have been supported since PHP 8.0

Multiple class methods adding to string

I have a string of data that will be written to a file as a log of events, so I need this string available from the time the page begins to load to the time it is completed loading, at which time the contents of the string are written to the log file.
I am able to add to this string across some classes, but not others so it has me confused. When it doesn't work I get an 'allowed memory size of ... exhausted' error.
FILE: index.php
spl_autoload_register(function($class) {
if (file_exists(dirname(__FILE__).'/classes/'.$class.'.class.php')) {
include dirname(__FILE__).'/classes/'.$class.'.class.php';
}
});
$App = new Core();
$App->Visitor->getIP(); // This will exhaust memory...why?
$App->Settings->hello(); // Works
$App->writeLog('in my index file...'); // Works
$App->viewLog();
FILE: /classes/Core.class.php
class Core {
public static $logContent;
public function __construct() {
$this->initialize();
}
private function initialize() {
self::$logContent = 'Lets start...';
$this->Visitor = new Visitor($this);
$this->Settings = new Settings($this);
$this->Cache = new Cache($this);
}
public function writeLog($action) {
self::$logContent .= $action;
}
public function viewLog() {
echo self::$logContent;
}
}
FILE: /classes/Visitor.class.php
class Visitor {
private $App;
public function __construct($App) {
$this->App = $App;
}
public function getIP() {
$this->App->writeLog('getting ip...'); // Exhausts memory
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
return false;
}
}
}
FILE: /classes/Settings.class.php
class Settings {
private $App;
public function __construct($App) {
$this->App = $App;
}
public function hello() {
$this->App->writeLog('getting ip...');
return 'hello';
}
}
What I can't figure out is that both Visitor.class.php and Settings.class.php are setup the same way with the same constructor and yet one will work and the other won't.
So as you can see, I made a static string that everything throughout the app can add to, then later this string will be written to a file one time. Am I going about this the wrong way?

Get Class to know a Variable

I'm facing a problem, I want the class Page to know the Variable '$format'.
// class1.php
<?php
include('./class2.php');
echo $format->getTest(); // returns :-) (declared in class2.php)
class Page {
PUBLIC function getText() {
return $format->getTest(); // returns Call to a member function getTest() on null
}
}
$page = new Page;
?>
// class2.php
<?php
class Format {
PUBLIC function getTest() {
return ":-)";
}
}
$format = new Format;
?>
Any suggestions/ideas?
EDIT:
I found a way: return $GLOBALS['format']->getTest();
But I dont like it, its so much to type. Any other way(s)?
Philip
Proper objective solution is to pass variable to constructor, setter or as argument to getText() method. Choose one you find most appropriate for your case.
Constructor
class Page
{
private $format;
public function __construct(Format $format)
{
$this->format = $format;
}
public function getText()
{
return $this->format->getTest();
}
}
$page = new Page($format);
echo $page->getText();
Setter
class Page
{
private $format;
public function setFormat(Format $format)
{
$this->format = $format;
}
public function getText()
{
return $this->format->getTest();
}
}
$page = new Page;
$page->setFormat($format);
echo $page->getText();
Argument
class Page
{
public function getText(Format $format)
{
return $format->getTest();
}
}
$page = new Page;
echo $page->getText($format);

How to user ThrowException matcher in PHPSpec?

I have problem in using ExceptionMatcher...My example spec:
class DescribeBall extends \PHPSpec\Context {
private $_ball = null;
function before() {
$this->_ball = $this->spec(new Ball);
}
function itShouldHaveStatusRolledOnRoll() {
$this->_ball->roll();
$this->_ball->getStatus()->should->be('Rolled');
}
function itShouldThrowException() {
$this->_ball->getException()->should->throwException('Exception','Error');
}
}
My example class
class Ball {
private $status = null;
public function roll() {
$this->status = 'Rolled';
}
public function getStatus() {
return $this->status;
}
public function getException() {
throw new Exception('Error');
}
}
Anyone used this matcher with success?
$this->_ball->getException()->should->throwException('Exception','Error');
Thanks to my colleagues:
"The last time I looked at it, it used closures (unless Marcello changed it meanwhile) it should still work like this":
function itShouldThrowException() {
$ball = $this->_ball;
$this->spec(function() use ($ball) {
$ball->getException();
})->should->throwException('Exception','Error');
}

Action_Helper in separte module does not get loaded

I followed this great article http://weierophinney.net/matthew/archives/246-Using-Action-Helpers-To-Implement-Re-Usable-Widgets.html, but currently i can't get work my simplified example.
PROBLEM The preDispatch does not get loaded.
I created new module user (there is also controller UserController, i hope this wont mess up the loading).
I have added two files in user.
Bootstrap.php - under module user
class User_Bootstrap extends Zend_Application_Module_Bootstrap {
public function initResourceLoader() {
$loader = $this->getResourceLoader();
$loader->addResourceType('helper', 'helpers', 'Helper');
}
protected function _initHelpers() {
Zend_Controller_Action_HelperBroker::addHelper(
new User_Helper_HandleLogin()
);
}
New folder under /user/helpers and class HandleLogin.
class User_Helper_HandleLogin extends Zend_Controller_Action_Helper_Abstract {
protected $view;
public function preDispatch() {
echo 'called';
if (($controller = $this->getActionController()) === null) {
return;
}
$this->createProfileWidget();
}
public function createProfileWidget() {
if (!$view = $this->getView()) {
return;
}
$view->user = '<h2>HELLO WORLD</h2>';
}
public function createLoginForm() {
}
public function getView() {
if ($this->view !== null) {
return $this->view;
}
$controller = $this->getActionController();
$view = $controller->view;
if (!$view instanceof Zend_View_Abstract) {
return;
}
//$view->addScriptPath(dirname(__FILE__) .'/../views/scripts');
$this->view = $view;
return $view;
}
}
And lastly added into layout.phtml the output.
<?php echo $this->user ?>
is init() function of User_Helper_HandleLogin works? is User_Bootstrap works? :) maybe you forget resources.modules[] = in config.ini?

Categories