Heres the code for the route:
<?php
use app\controllers\Home;
use app\core\App;
$app = new App();
$app->router->get('/', function(){
echo "hello there";
});
$app->router->get('user/', 'user');
$app->router->get('/contact', [Home::class,'contacts']);
$app->router->post('/contact', function(){
echo "handlecontact";
});
$app->run();
Heres the code for the router core class:
<?php
namespace app\core;
class router{
public Request $request;
public Response $response;
protected array $routes = [];
public function __construct(Request $request,Response $response)
{
$this->request = $request;
$this->response = $response;
}
public function get($path, $callback){
$this->routes['get'][$path] = $callback;
}
public function post($path, $callback){
$this->routes['post'][$path] = $callback;
}
public function resolve(){
$path = $this->request->getpath();
$method = $this->request->getmethod();
$callback = $this->routes[$method][$path] ?? false;
if($callback == false){
$this->response->setstatuscode(404);
return "not found";
}
if(is_array($callback)){
$callback[0] = new $callback[0]();
}
return call_user_func($callback,$this->request);
}
I want to be able to declare a route and pass a parameter to it. If i do it so like that it tells me route not found.
Like i want to be able to write the route like this:
$app->router->get('/contact/{id}', function(){
echo $id; // to echo it out like this from the controller
});
Can someone be of good help or also add something to the idea. Thanks in advance
Related
So my issue is, I have no idea how to handle page not found handling, Since routing runs every time a route is added if you do anything other than the first route it'll have 2 outputs.
Routes.php
Route::set('index.php', function() {
Index::CreateView('Index');
});
Route::set('test', function() {
Test::CreateView('Test');
});
?>
Routes.php (class)
<?php
class Route {
public static $validRoutes = array();
public static function set($route, $function) {
self::$validRoutes[] = $route;
$url = $_GET['url'];
if($url == $route) {
$function->__invoke();
die();
}
if(!in_array($url, self::$validRoutes)) {
Controller::CreateView("404");
die();
}
}
}
?>
I'm trying to understand how I'd even handle if its not found.
How about something like:
<?php
class Router
{
public $routes = [];
public function add($route, $function)
{
$this->routes[$route] = $function;
}
public function route($path)
{
$function =
$this->routes[$path] ??
$this->routes['404'] ?? null;
if(is_null($function))
http_response_code(404) && exit('404 Not Found.');
$function->__invoke();
}
}
$router = new Router;
$router->add('foo', function() {
echo 'bar';
});
$router->add('greeting', function() {
echo 'hello earth';
});
$router->route('greeting');
Output:
hello earth
Rather than trying to resolve your routes upon each addition, you can add them all, and then resolve/dispatch/route later.
I've simplified the Router::routes array to use the paths as keys.
When resolving, if the path cannot be found (the index does not exist) it will try and check for a '404' key in the routes array. Failing that it responds with a basic 404.
What's wrong with my code? I can't pass variable from my Auth middleware to controller. In controller, the attribute "null".
Index:
$c = new \Slim\Container($configuration);
$api = new \Slim\App($c);
$api->group('/', function () use ($api) {
$this->post('login', '\AuthController:login');
...
$this->post('getIngredientsCategories', '\IngredientsController:getIngredientsCategories');
})->add(new ApiAuthenticateController());
$api->run();
Middleware (ApiAuthenticateController)
use \Firebase\JWT\JWT;
class ApiAuthenticateController
{
public function __invoke($request, $response, $next)
{
$jwtDecoded = null;
$req = $request->getUri()->getPath();
$_req = RequestValidatorController::isRequestEnabled($req);
if ($_req !== false) {
if ($_req['login_required']) {
$jwt = filter_var($request->getHeaderLine('AUTHORIZATION'), FILTER_SANITIZE_STRING);
$jwt = explode(" ", $jwt)[1];
try {
$jwtDecoded = JWT::decode($jwt, JWT_SECRET, array('HS256'));
} catch (\Exception $e) {
return $this->deny_access($response);
}
}
$request = $request->withAttribute('foo', $jwtDecoded);
//HERE - attribute "foo" in $request exists - checked by var_dump()..
$response = $next($request, $response);
$response = $response->withHeader('Content-type', 'application/json');
return $response;
} else {
return $this->deny_access($response);
}
}
IngredientsController
class IngredientsController extends Controller
{
private $_ingredients;
public function __construct(\Interop\Container\ContainerInterface $container)
{
parent::__construct($container);
}
}
Controller
class Controller
{
private $request;
private $response;
protected $data;
protected $method;
protected $user;
public function __construct(Interop\Container\ContainerInterface $container)
{
$this->request = $container->get('request');
$this->response = $container->get('response');
$this->data = (object)Tools::stripInput($this->request->getParsedBody());
$this->method = $this->request->getUri()->getPath();
$this->user = $this->request->getAttribute('foo');
var_dump($this->user);
// returns NULL. if $this->request->getAttributes()... returns empty
}
}
Please note, that code is "cleaned" for this post and for better readability, some functions and conditions where deleted. Code is working - sorry for possible typos.
the problem is coming from here
in the middleware ApiAuthenticateController, you should change the code like this
$request = $request->withAttribute('foo', $jwtDecoded);
// you should return the $next here not to do anything else
return $next($request, $response);
and in your IngredientsController:getIngredientsCategories part of code you should return the
function getIngredientsCategories ($request, $response, $args){
//do your job...
$response = $response->withHeader('Content-type', 'application/json');
return $response;
}
and finally, in the constructor, you have don't have the actual $request after the middleware I think so you would have the $this->request->getAttributes() inside your getIngredientsCategories function
Hope it helps.
I've been handed a PHP class, and I'm not interested in fully restructuring it. (it works!)
But I'd like to add a slight modification inside a few methods.
Here is one of the many methods inside the class:
<?php
class SomeFunClass {
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$response = $this->execute($request);
return $response;
}
}
?>
The return $response is a string value.
I've come to a point that I need to return the $request string, which happens to be a json string.
The prepareRequest() method always returns a json string, which is then passed to the exec() method, which simply sends the data via cURL to a domain.
I'd like to extract the $request string (when I call the getAccountInfo() method), for later review.
Here's what I'm doing now:
<?php
$api = new SomeFunClass();
$curlresponse = $api->getAccountInfo();
?>
Obviously, the example immediately above only gives me back what the cURL response would be.
Would be nice to call a method that lets me see what the $request looks like.
I'm open to suggestions.
Just return an array with the request and the response:
<?php
class SomeFunClass {
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$response = $this->execute($request);
return array('request' => $request, 'response' => $response);
}
}
?>
You can modify those methods to store the last request into an attribute of the current class :
<?php
class SomeFunClass {
$last_request;
...
public function getAccountInfo()
{
$request = $this->prepareRequest('get_account_info');
$last_request = request;
$response = $this->execute($request);
return $response;
}
public function getLastRequest()
{
return $this -> last_request;
}
}
?>
Or, better, if prepareRequest is a method of yours, then just modify this one to store the last request.
You can do something like this:
<?php
class SomeFunClass {
public $request;
public $response;
public function getAccountInfo()
{
$this->request = $this->prepareRequest('get_account_info');
$this->response = $this->execute($this->request);
return $this->response;
}
}
?>
Now, you can do something like this:
<?php
$api = new SomeFunClass();
$curlresponse = $api->getAccountInfo();
$request = $api->request;
?>
Ideally, you can do implement your class like this to take actual advantage of OOP (so that these instance variables request and response are auto-set for all your methods):
<?php
class SomeFunClass {
public $request;
public $response;
public function getAccountInfo()
{
$this->prepareRequest('get_account_info');
return $this->execute();
}
public function anotherMethod()
{
$this->prepareRequest('another_method', 'some', 'args');
return $this->execute();
}
public function prepareRequest()
{
$args = func_get_args(); // contains your arguments
$method = array_shift($args); // contains your method name
...
...
$this->request = $return // value returned by this method
}
public function execute()
{
$request = $this->request;
...
...
$this->response = $return // value returned by this method
}
}
?>
You could also do this:
<?php
class SomeFunClass {
public function reviewRequest($request)
{
return $this->prepareRequest($request);
}
}
And then:
<?php
$api = new SomeFunClass();
$request = $api->reviewRequest('get_account_info');
Hello I'm trying to implement a url router in php something familiar to express.js
Here is the code I have so far.
class Router{
private $request;
private $request_params;
private $params_num;
private $paths;
public function __construct(){
$this->paths = array();
$this->request = strtolower($this->hookRequest());
if ($this->request != false){
$this->request_params = $this->hookRequestParams();
} else {
$this->request_params = array('home');
}
}
public function __destruct(){
foreach($this->paths as $key => $value){
if($this->getRequest() == $key){
$value();
}
}
}
public function get($path, $func){
$this->paths[$path] = $func;
}
private function hookRequest(){
return isset($_GET['req']) ? rtrim($_GET['req'], '/') : false;
}
private function hookRequestParams(){
$params = explode('/', $this->request);
$this->params_num = count($params);
return $params;
}
public function getRequest(){
return $this->request;
}
public function getRequestParams(){
return $this->request_params;
}
public function getPage(){
return $this->request_params[0];
}
public function getAction(){
if($this->params_num > 1){
return $this->request_params[1];
}
return false;
}
public function getActionParams(){
if($this->params_num > 2){
return $this->request_params[2];
}
return false;
}
}
This is used like this as you can imagine:
$router = new Router();
$router->get('index', function(){
echo 'index'; //index is being shown to the browser as expectd
echo $this->getPage(); // This does not work apparently
})
My issue is how could I execute $router methods from within the anonymous function?
As shown in this example with $this->getPAge();
Use a closure..
$router->get('index', function() use ($router) {
echo 'index';
echo $router->getPage();
})
If you define your Closure within your class, $this should be work.
in zf1, we can get controller and action name using
$controller = $this->getRequest()->getControllerName();
$action = $this->getRequest()->getActionName();
How we can achieve this in zf2?
UPDATE:
I tried to get them using
echo $this->getEvent()->getRouteMatch()->getParam('action', 'NA');
echo $this->getEvent()->getRouteMatch()->getParam('controller', 'NA');
But I am getting error
Fatal error: Call to a member function getParam() on a non-object
I like to get them in __construct() method;
Ideally I would like to check if there is no Action is defined it will execute noaction() method. I would check using php method method_exists.
Even simpler:
$controllerName =$this->params('controller');
$actionName = $this->params('action');
you can't access these variables in controller __construct() method, but you can access them in dispatch method and onDispatch method.
but if you would like to check whether action exist or not, in zf2 there is already a built in function for that notFoundAction as below
public function notFoundAction()
{
parent::notFoundAction();
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setContent("Action not found");
return $response;
}
but if you still like to do it manually you can do this using dispatch methods as follow
namespace Mynamespace\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Stdlib\ResponseInterface as Response;
use Zend\Mvc\MvcEvent;
class IndexController extends AbstractActionController
{
public function __construct()
{
}
public function notFoundAction()
{
parent::notFoundAction();
$response = $this->getResponse();
$response->setStatusCode(200);
$response->setContent("Action not found");
return $response;
}
public function dispatch(Request $request, Response $response = null)
{
/*
* any customize code here
*/
return parent::dispatch($request, $response);
}
public function onDispatch(MvcEvent $e)
{
$action = $this->params('action');
//alertnatively
//$routeMatch = $e->getRouteMatch();
//$action = $routeMatch->getParam('action', 'not-found');
if(!method_exists(__Class__, $action."Action")){
$this->noaction();
}
return parent::onDispatch($e);
}
public function noaction()
{
echo 'action does not exits';
}
}
You will get module , controller and action name like this in Zf2 inside your controller...
$controllerClass = get_class($this);
$moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
$tmp = substr($controllerClass, strrpos($controllerClass, '\\')+1 );
$controllerName = str_replace('Controller', "", $tmp);
//set 'variable' into layout...
$this->layout()->currentModuleName = strtolower($moduleNamespace);
$this->layout()->currentControllerName = strtolower($controllerName);
$this->layout()->currentActionName = $this->params('action');
$controllerName = strtolower(Zend_Controller_Front::getInstance()->getRequest()->getControllerName());