NotionCommotion Posted November 23, 2017 Share Posted November 23, 2017 Trying to build a simple router. Still needs work, but one place I am stuck on is where I define my endpoints in index.php. Specifically, how do I access $router when within the callback? I see that my use of $this is incorrect as I am not in object context. What do I need to change to make it in object context? Thanks $router->get('pattern', function(Request $request){ //How do I access $this? }); index.php <?php error_reporting(E_ALL); ini_set('display_startup_errors', 1); ini_set('display_errors', 1); spl_autoload_register(function ($classname) { $parts = explode('\\', $classname); require __DIR__."/../src/".end($parts).".php"; }); $c = new Pimple(); $c['view'] = function ($c) { return new View(); }; $router = new Router($c); $router->get('/^\/test\/?$/', function(Request $request, Response $response){ var_dump($request); var_dump($response); }); $router->get('/^\/test\/(\w+)\/?$/', function(Request $request, Response $response){ var_dump($request); var_dump($response); }); $router->get('/^\/test\/(\w+)\/(\d+)\/?$/', function(Request $request, Response $response){ var_dump($request); var_dump($response); }); $router->get('/^\/index.php\/?$/', function(Request $request){ var_dump($this);var_dump($request); var_dump($response); }); $router->run(); <?php class Router { private $routes = []; private $container = []; public function __construct($container) { $this->container=$container; } public function get($pattern, $callback) { $this->routes['GET'][$pattern] = $callback; } public function post($pattern, $callback) { $this->routes['POST'][$pattern] = $callback; } public function put($pattern, $callback) { $this->routes['PUT'][$pattern] = $callback; } public function delete($pattern, $callback) { $this->routes['DELETE'][$pattern] = $callback; } public function run() { $uri=$_SERVER['REQUEST_URI']; foreach ($this->routes[$_SERVER['REQUEST_METHOD']] as $pattern => $callback) { if (preg_match($pattern, $uri, $params) === 1) { return call_user_func($callback, new Request($params)); } } } } <?php class Request { private $params = []; private $uri; public function __construct(array $params) { $this->uri=$params[0]; array_shift($params); $this->params=array_values($params); } public function getUri() { return $this->uri; } public function getParams() { return $this->params; } public function getData() { switch($_SERVER['REQUEST_METHOD']){ case 'GET':$data=$_GET;break; case 'POST':$data=$_POST;break; case 'PUT': parse_str(file_get_contents("php://input"),$data); /* //Or do it per http://php.net/manual/en/features.file-upload.put-method.php? $putdata = fopen("php://input", "r"); // Read the data 1 KB at a time and write to a stream while ($data = fread($putdata, 1024)) { fwrite($fp, $data); } fclose($fp); */ break; case 'DELETE': //Can a delete method have data? Is it the same as $_GET? $data=$_GET; break; } return $data; } } Quote Link to comment Share on other sites More sharing options...
kicken Posted November 24, 2017 Share Posted November 24, 2017 Either use it $router->get('/^\/test\/?$/', function(Request $request, Response $response) use ($router){ var_dump($request); var_dump($response); }); or have your router pass it into the callback as a parameter. $router->get('/^\/test\/?$/', function(Request $request, Response $response, Router $router){ var_dump($request); var_dump($response); }); if (preg_match($pattern, $uri, $params) === 1) { return call_user_func($callback, new Request($params), $this); } On another note, your callback examples show them taking a Response parameter, but your router code provides no such parameter, only a Request. Either add a response parameter to your call_user_func code or remove it from your callback parameter lists. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted November 24, 2017 Author Share Posted November 24, 2017 Thanks kicken, I had tried your second solution but tried to assign the passed $this to variable name $this in the callback, but as I am sure you know, it wouldn't work. How do you think Slim allows $this to be used in the callback? Yes, I know about the missing Response parameter. Going back and forth on whether I should include it, and got side tracked on $this issue. PS. Sorry for the poor post title. Just realized now that it was only "Access". Not what I meant to use. If any mod can change it, please do to something like "Accessing $this in a callback". Thanks! https://www.slimframework.com/docs/objects/router.html#how-to-create-routes Closure bindingIf you use a Closure instance as the route callback, the closure’s state is bound to the Container instance. This means you will have access to the DI container instance inside of the Closure via the $this keyword: $app = new \Slim\App();$app->get('/hello/{name}', function ($request, $response, $args) {// Use app HTTP cookie service$this->get('cookies')->set('name', ['value' => $args['name'],'expires' => '7 days']);}); Quote Link to comment Share on other sites More sharing options...
kicken Posted November 24, 2017 Share Posted November 24, 2017 You can use Closure::call (7+) or Closure::bindTo (5.4+) to execute a closure with a specific $this value. if (preg_match($pattern, $uri, $params) === 1) { if ($callback instancof \Closure){ return $closure->call($this, new Request($params)); } else { return call_user_func($callback, new Request($params), $this); } } I'm generally not a fan of such shenanigans because it can lead to confusion. <?php class Router { private $routeList; public function addRoute($url, $callback){ $this->routeList[$url] = $callback; } public function route($url){ $this->routeList[$url]->call($this, $url); } } class HomeController { public function __construct($router){ $router->addRoute('/', function(){ return $this->doHomePage(); }); } private function doHomePage(){ $quote = $this->getRandomQuote(); return new Response('home.html', ['quote' => $quote]); } private function getRandomQuote(){ $quotes = ['You can do anything, but not everything.—David Allen', ' The richest man is not he who has the most, but he who needs the least. —Unknown Author','You miss 100 percent of the shots you never take.—Wayne Gretzky']; return $quotes[array_rand($quotes)]; } } class Response { public function __construct($template, $data){ } } $router = new Router(); $controller = new HomeController($router); $router->route('/'); Fatal error: Uncaught Error: Call to undefined method Router::doHomePage() in /in/WlsRe:17 Looking just at the HomeController class there's no reason why it shouldn't work. the doHomePage method is clearly defined and everyone knows that $this is a reference to the current object, so WTF?!. It's not until you dig into the code for Router (or read docs if they exist) that you realize that it's changing what $this is. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted November 26, 2017 Author Share Posted November 26, 2017 Good example. Simple is good and some of these tricks can definitely was a lot of time Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.