Jump to content

Making object available to closure


NotionCommotion

Recommended Posts

By using use ($methods), $methods is available within the closure.

 

Can I instead pass new Methods() to Router and somehow access the methods?

require_once '../classes/Methods.php';
$methods=new Methods();
require_once '../classes/Router.php';
//$router = new Router(new Methods());
$router = new Router();


$router->get('/^\/?$/', function(){
    echo('home');
 });
$router->get('/^\/entity\/?$/', function() use ($methods){
    echo('display entity');
    echo("<pre>".print_r(get_class_methods($methods),1).'</pre>');
 });
$router->post('/^\/entity\/?$/', function(){
    echo('save');
 });
$router->get('/^\/entity\/(\w+)\/(\d+)\/?$/', function($type, $id) use ($methods){
    echo("display type $type with id $id");
    echo("<pre>".print_r(get_class_methods($methods),1).'</pre>');
 });
$router->execute($_SERVER['REQUEST_URI']);
class Router {


    private $routesGet = array();
    private $routesPost = array();


    //public function __construct($methods){$this->methods=$methods;}


    public function get($pattern, $callback) {
        $this->routesGet[$pattern] = $callback;
    }
    public function post($pattern, $callback) {
        $this->routesPost[$pattern] = $callback;
    }


    public function execute($uri) {
        switch($_SERVER['REQUEST_METHOD']) {
            case 'GET':
                foreach ($this->routesGet as $pattern => $callback) {
                    if (preg_match($pattern, $uri, $params) === 1) {
                        array_shift($params);
                        return call_user_func_array($callback, array_values($params));
                    }
                }
                break;
            case 'POST':
                foreach ($this->routesPost as $pattern => $callback) {
                    if (preg_match($pattern, $uri, $params) === 1) {
                        array_shift($params);
                        return call_user_func_array($callback, array_values($params));
                    }
                }
                break;
                defaut: trigger_error('Method "'.$_SERVER['REQUEST_METHOD'].'" is not supported', E_USER_ERROR);
        }
    }


}

 

Link to comment
Share on other sites

Short answer: no, you can't.

 

Long answer: no, you can't, but if it were I still wouldn't do it. The router you have now is fairly minimal (not necessarily a bad thing) and adding a concept of a sort of... I forget what it's called but it's an object you pass around that contains arbitrary data... would over-complicate the class and its behavior. Use-ing the variable makes sense to me and I would do that.

 

That said, what you're doing now doesn't make sense. Based on context I assume $methods just a placeholder for more work? Because if it really is merely a new Methods() then you should just do that inside each callback.

Link to comment
Share on other sites

Thanks for the short and even more so long answers!  I've used Slim which I like, but for this particular project wanted something even slimmer, and will definitely not complicate efforts just to find some way to do so.

 

$methods is just to allow little code to be included in the callback so it is more readable.  You mean create the object within each callback?  If so, I agree, and will do the later.

 

$router->post('/^\/entity\/?$/', function() use ($methods) {
    $rsp=$methods->saveSomething($_POST);   //Returns [data, responseCode]
    http_response_code($rsp[1]);
    header('Content-Type: application/json');
    echo json_encode($rsp[0]);
});


$router->post('/^\/entity\/?$/', function() {
    $mySpecificObject=new MySpecificClass();
    $rsp=$mySpecificObject->saveSomething($_POST);   //Returns [data, responseCode]
    http_response_code($rsp[1]); //Will actually use a small method for these three lines...
    header('Content-Type: application/json');
    echo json_encode($rsp[0]);
});

 

Link to comment
Share on other sites

Technically you could do what you propose by re-binding the closure within the router, but I'd argue that doing such a thing would ultimately make your code more difficult to write and maintain, you're better off just using $methods like you are now.

 

 

Bad code, don't do this

 

<?php

class Methods {
    public function doSomething(){
        echo 'Did something';
    }
}

class Router {
   public $methods;
   public function __construct($methods){
        $this->methods = $methods;
   }
   
   public function execute($fn){
       return call_user_func($fn->bindTo($this));
   }
}

$router = new Router(new Methods);
$router->execute(function(){
    echo 'Running the closure';
    $this->methods->doSomething();
});

 

 

 

Your last three methods you could move out either into the router or after your $router->execute() call. For example create some sort of Response object that just holds the status code, headers, and body and have your routes return that.

<?php

class Response {
    private $status;
    private $headers;
    private $body;

    public function __construct($status, $body, $headers = []){
        $this->status = $status;
        $this->headers = $headers;
        $this->body = $body;
    }

    public function output(){
        http_response_code($this->status);
        foreach ($headers as $header){
            header($header);
        }

        echo $this->body;
    }
}

class JSONResponse extends Response {
    public function __construct($status, $data, $headers){
        $headers[] = 'Content-type: application/json';
        $body = json_encode($body);
        parent::__construct($status, $body, $headers);
    }
}

// ------------------------------ //

$router->post('/^\/entity\/?$/', function() {
    $mySpecificObject=new MySpecificClass();
    $rsp=$mySpecificObject->saveSomething($_POST);   //Returns [data, responseCode]

    return new JSONResponse($rsp[1], $rsp[0]);
});

// ------------------------------ //

$response = $router->execute($_SERVER['REQUEST_URI']);
$response->output();
  • Like 1
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.