A fast & flexible router

Overview

Klein.php

Build Status

klein.php is a fast & flexible router for PHP 5.3+

Getting started

  1. PHP 5.3.x is required
  2. Install Klein using Composer (recommended) or manually
  3. Setup URL rewriting so that all requests are handled by index.php
  4. (Optional) Throw in some APC for good measure

Composer Installation

  1. Get Composer
  2. Require Klein with php composer.phar require klein/klein
  3. Add the following to your application's main PHP file: require 'vendor/autoload.php';

Example

Hello World - Obligatory hello world example

<?php
require_once __DIR__ . '/vendor/autoload.php';

$klein = new \Klein\Klein();

$klein->respond('GET', '/hello-world', function () {
    return 'Hello World!';
});

$klein->dispatch();

Example 1 - Respond to all requests

$klein->respond(function () {
    return 'All the things';
});

Example 2 - Named parameters

$klein->respond('/[:name]', function ($request) {
    return 'Hello ' . $request->name;
});

Example 3 - So RESTful

$klein->respond('GET', '/posts', $callback);
$klein->respond('POST', '/posts', $callback);
$klein->respond('PUT', '/posts/[i:id]', $callback);
$klein->respond('DELETE', '/posts/[i:id]', $callback);
$klein->respond('OPTIONS', null, $callback);

// To match multiple request methods:
$klein->respond(array('POST','GET'), $route, $callback);

// Or you might want to handle the requests in the same place
$klein->respond('/posts/[create|edit:action]?/[i:id]?', function ($request, $response) {
    switch ($request->action) {
        //
    }
});

Example 4 - Sending objects / files

$klein->respond(function ($request, $response, $service) {
    $service->xml = function ($object) {
        // Custom xml output function
    }
    $service->csv = function ($object) {
        // Custom csv output function
    }
});

$klein->respond('/report.[xml|csv|json:format]?', function ($request, $response, $service) {
    // Get the format or fallback to JSON as the default
    $send = $request->param('format', 'json');
    $response->$send($report);
});

$klein->respond('/report/latest', function ($request, $response, $service) {
    $response->file('/tmp/cached_report.zip');
});

Example 5 - All together

$klein->respond(function ($request, $response, $service, $app) use ($klein) {
    // Handle exceptions => flash the message and redirect to the referrer
    $klein->onError(function ($klein, $err_msg) {
        $klein->service()->flash($err_msg);
        $klein->service()->back();
    });

    // The fourth parameter can be used to share scope and global objects
    $app->db = new PDO(...);

    // $app also can store lazy services, e.g. if you don't want to
    // instantiate a database connection on every response
    $app->register('db', function() {
        return new PDO(...);
    });
});

$klein->respond('POST', '/users/[i:id]/edit', function ($request, $response, $service, $app) {
    // Quickly validate input parameters
    $service->validateParam('username', 'Please enter a valid username')->isLen(5, 64)->isChars('a-zA-Z0-9-');
    $service->validateParam('password')->notNull();

    $app->db->query(...); // etc.

    // Add view properties and helper methods
    $service->title = 'foo';
    $service->escape = function ($str) {
        return htmlentities($str); // Assign view helpers
    };

    $service->render('myview.phtml');
});

// myview.phtml:
<title><?php echo $this->escape($this->title) ?></title>

Route namespaces

$klein->with('/users', function () use ($klein) {

    $klein->respond('GET', '/?', function ($request, $response) {
        // Show all users
    });

    $klein->respond('GET', '/[:id]', function ($request, $response) {
        // Show a single user
    });

});

foreach(array('projects', 'posts') as $controller) {
    // Include all routes defined in a file under a given namespace
    $klein->with("/$controller", "controllers/$controller.php");
}

Included files are run in the scope of Klein ($klein) so all Klein methods/properties can be accessed with $this

Example file for: "controllers/projects.php"

// Routes to "/projects/?"
$this->respond('GET', '/?', function ($request, $response) {
    // Show all projects
});

Lazy services

Services can be stored lazily, meaning that they are only instantiated on first use.

<?php
$klein->respond(function ($request, $response, $service, $app) {
    $app->register('lazyDb', function() {
        $db = new stdClass();
        $db->name = 'foo';
        return $db;
    });
});

//Later

$klein->respond('GET', '/posts', function ($request, $response, $service, $app) {
    // $db is initialised on first request
    // all subsequent calls will use the same instance
    return $app->lazyDb->name;
});

Validators

To add a custom validator use addValidator($method, $callback)

$service->addValidator('hex', function ($str) {
    return preg_match('/^[0-9a-f]++$/i', $str);
});

You can validate parameters using is<$method>() or not<$method>(), e.g.

$service->validateParam('key')->isHex();

Or you can validate any string using the same flow..

$service->validate($username)->isLen(4,16);

Validation methods are chainable, and a custom exception message can be specified for if/when validation fails

$service->validateParam('key', 'The key was invalid')->isHex()->isLen(32);

Routing

[ match_type : param_name ]

Some examples

*                    // Match all request URIs
[i]                  // Match an integer
[i:id]               // Match an integer as 'id'
[a:action]           // Match alphanumeric characters as 'action'
[h:key]              // Match hexadecimal characters as 'key'
[:action]            // Match anything up to the next / or end of the URI as 'action'
[create|edit:action] // Match either 'create' or 'edit' as 'action'
[*]                  // Catch all (lazy)
[*:trailing]         // Catch all as 'trailing' (lazy)
[**:trailing]        // Catch all (possessive - will match the rest of the URI)
.[:format]?          // Match an optional parameter 'format' - a / or . before the block is also optional

Some more complicated examples

/posts/[*:title][i:id]     // Matches "/posts/this-is-a-title-123"
/output.[xml|json:format]? // Matches "/output", "output.xml", "output.json"
/[:controller]?/[:action]? // Matches the typical /controller/action format

Note - all routes that match the request URI are called - this allows you to incorporate complex conditional logic such as user authentication or view layouts. e.g. as a basic example, the following code will wrap other routes with a header and footer

$klein->respond('*', function ($request, $response, $service) { $service->render('header.phtml'); });
//other routes
$klein->respond('*', function ($request, $response, $service) { $service->render('footer.phtml'); });

Routes automatically match the entire request URI. If you need to match only a part of the request URI or use a custom regular expression, use the @ operator. If you need to negate a route, use the ! operator

// Match all requests that end with '.json' or '.csv'
$klein->respond('@\.(json|csv)$', ...

// Match all requests that _don't_ start with /admin
$klein->respond('[email protected]^/admin/', ...

Views

You can send properties or helpers to the view by assigning them to the $service object, or by using the second arg of $service->render()

$service->escape = function ($str) {
    return htmlentities($str);
};

$service->render('myview.phtml', array('title' => 'My View'));

// Or just: $service->title = 'My View';

myview.phtml

<title><?php echo $this->escape($this->title) ?></title>

Views are compiled and run in the scope of $service so all service methods can be accessed with $this

$this->render('partial.html')           // Render partials
$this->sharedData()->get('myvar')       // Access stored service variables
echo $this->query(array('page' => 2))   // Modify the current query string

API

Below is a list of the public methods in the common classes you will most likely use. For a more formal source of class/method documentation, please see the PHPdoc generated documentation.

$request->
    id($hash = true)                    // Get a unique ID for the request
    paramsGet()                         // Return the GET parameter collection
    paramsPost()                        // Return the POST parameter collection
    paramsNamed()                       // Return the named parameter collection
    cookies()                           // Return the cookies collection
    server()                            // Return the server collection
    headers()                           // Return the headers collection
    files()                             // Return the files collection
    body()                              // Get the request body
    params()                            // Return all parameters
    params($mask = null)                // Return all parameters that match the mask array - extract() friendly
    param($key, $default = null)        // Get a request parameter (get, post, named)
    isSecure()                          // Was the request sent via HTTPS?
    ip()                                // Get the request IP
    userAgent()                         // Get the request user agent
    uri()                               // Get the request URI
    pathname()                          // Get the request pathname
    method()                            // Get the request method
    method($method)                     // Check if the request method is $method, i.e. method('post') => true
    query($key, $value = null)          // Get, add to, or modify the current query string
    <param>                             // Get / Set (if assigned a value) a request parameter

$response->
    protocolVersion($protocol_version = null)       // Get the protocol version, or set it to the passed value
    body($body = null)                              // Get the response body's content, or set it to the passed value
    status()                                        // Get the response's status object
    headers()                                       // Return the headers collection
    cookies()                                       // Return the cookies collection
    code($code = null)                              // Return the HTTP response code, or set it to the passed value
    prepend($content)                               // Prepend a string to the response body
    append($content)                                // Append a string to the response body
    isLocked()                                      // Check if the response is locked
    requireUnlocked()                               // Require that a response is unlocked
    lock()                                          // Lock the response from further modification
    unlock()                                        // Unlock the response
    sendHeaders($override = false)                  // Send the HTTP response headers
    sendCookies($override = false)                  // Send the HTTP response cookies
    sendBody()                                      // Send the response body's content
    send()                                          // Send the response and lock it
    isSent()                                        // Check if the response has been sent
    chunk($str = null)                              // Enable response chunking (see the wiki)
    header($key, $value = null)                     // Set a response header
    cookie($key, $value = null, $expiry = null)     // Set a cookie
    cookie($key, null)                              // Remove a cookie
    noCache()                                       // Tell the browser not to cache the response
    redirect($url, $code = 302)                     // Redirect to the specified URL
    dump($obj)                                      // Dump an object
    file($path, $filename = null)                   // Send a file
    json($object, $jsonp_prefix = null)             // Send an object as JSON or JSONP by providing padding prefix

$service->
    sharedData()                                    // Return the shared data collection
    startSession()                                  // Start a session and return its ID
    flash($msg, $type = 'info', $params = array()   // Set a flash message
    flashes($type = null)                           // Retrieve and clears all flashes of $type
    markdown($str, $args, ...)                      // Return a string formatted with markdown
    escape($str)                                    // Escape a string
    refresh()                                       // Redirect to the current URL
    back()                                          // Redirect to the referer
    query($key, $value = null)                      // Modify the current query string
    query($arr)
    layout($layout)                                 // Set the view layout
    yieldView()                                     // Call inside the layout to render the view content
    render($view, $data = array())                  // Render a view or partial (in the scope of $response)
    partial($view, $data = array())                 // Render a partial without a layout (in the scope of $response)
    addValidator($method, $callback)                // Add a custom validator method
    validate($string, $err = null)                  // Validate a string (with a custom error message)
    validateParam($param, $err = null)                  // Validate a param
    <callback>($arg1, ...)                          // Call a user-defined helper
    <property>                                      // Get a user-defined property

$app->
    <callback>($arg1, ...)                          //Call a user-defined helper

$validator->
    notNull()                           // The string must not be null
    isLen($length)                      // The string must be the exact length
    isLen($min, $max)                   // The string must be between $min and $max length (inclusive)
    isInt()                             // Check for a valid integer
    isFloat()                           // Check for a valid float/decimal
    isEmail()                           // Check for a valid email
    isUrl()                             // Check for a valid URL
    isIp()                              // Check for a valid IP
    isAlpha()                           // Check for a-z (case insensitive)
    isAlnum()                           // Check for alphanumeric characters
    contains($needle)                   // Check if the string contains $needle
    isChars($chars)                     // Validate against a character list
    isRegex($pattern, $modifiers = '')  // Validate against a regular expression
    notRegex($pattern, $modifiers ='')
    is<Validator>()                     // Validate against a custom validator
    not<Validator>()                    // The validator can't match
    <Validator>()                       // Alias for is<Validator>()

Unit Testing

Unit tests are a crucial part of developing a routing engine such as Klein. Added features or bug-fixes can have adverse effects that are hard to find without a lot of testing, hence the importance of unit testing.

This project uses PHPUnit as its unit testing framework.

The tests all live in /tests and each test extends an abstract class AbstractKleinTest

To test the project, simply run php composer.phar install --dev to download a common version of PHPUnit with composer and run the tests from the main directory with ./vendor/bin/phpunit

Contributing

See the contributing guide for more info

More information

See the wiki for more information

Contributors

License

(MIT License)

Copyright (c) 2010 Chris O'Hara [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Comments
  • Configuration issue?

    Configuration issue?

    I know this isn't a "I can't get this working" forum, but I've found it very difficult to get started with this project.

    I have an index.php file inside demo-brett/listing with the following .htaccess

    # from https://gist.github.com/chriso/874000
    
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule . index.php [L]
    

    I'm using the single-file implementation to skip the autoloading headache like so,

    /**
     * PHP Router called Klein (v2.0.2)
     *
     * Website:
     *      https://github.com/chriso/klein.php
     * Single file implmentation:
     *      https://raw2.github.com/gbouthenot/klein.php/K2-singlefile/src-single/klein.php
     * Readme:
     *      https://github.com/chriso/klein.php/blob/v2.0.2/README.md
    */
    require_once('klein.php');
    
    $klein = new \Klein\Klein();
    

    But I can't seem to respond to absolutely anything, except for the generic "everything" response.

    $klein->respond('/[:name]', function ($request) {
        return 'Hello ' . $request->name;
    });
    
    $klein->respond('GET', '/', function ($request) {
        return '[GET]';
    });
    
    $klein->respond('POST', '/', function ($request) {
        return '[POST]';
    });
    
    $klein->respond('GET', 'hello-world', function ($request) {
        return '[GET 1] Hello world';
    });
    
    $klein->respond('POST', 'hello-world', function ($request) {
        return '[POST 1] Hello world';
    });
    
    $klein->respond('GET', '/hello-world', function ($request) {
        return '[GET 2] Hello world';
    });
    
    $klein->respond('POST', '/hello-world', function ($request) {
        return '[POST 2] Hello world';
    });
    
    $klein->respond('/[:name]', function ($request) {
        return 'Hello ' . $request->name;
    });
    
    $klein->respond(function () {
        return 'All the things';
    });
    

    I'm then dispatching the response of course, but all I ever receive is All the things

    /**
     * Generate response
     */
    $klein->dispatch();
    

    I have all the variables dumping at the bottom and nothing seems to be contained in $_GET or $_POST, only in $_SERVER

    /**
     * Temporary profiling
     */
    echo '<pre>';
    var_dump($_GET);
    var_dump($_POST);
    var_dump($_SERVER);
    

    If I visit http://localhost/demo-brett/listing/hello-world, I receive the following output,

    array(0) {
    }
    array(0) {
    }
    array(31) {
      ["REDIRECT_STATUS"]=>
      string(3) "200"
      ["HTTP_HOST"]=>
      string(24) "localhost"
      ["HTTP_CONNECTION"]=>
      string(10) "keep-alive"
      ["HTTP_CACHE_CONTROL"]=>
      string(9) "max-age=0"
      ["HTTP_ACCEPT"]=>
      string(74) "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
      ["HTTP_USER_AGENT"]=>
      string(119) "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36"
      ["HTTP_DNT"]=>
      string(1) "1"
      ["HTTP_ACCEPT_ENCODING"]=>
      string(17) "gzip,deflate,sdch"
      ["HTTP_ACCEPT_LANGUAGE"]=>
      string(26) "en-CA,en;q=0.8,en-US;q=0.6"
      ["SERVER_NAME"]=>
      string(24) "localhost"
      ["SERVER_ADDR"]=>
      string(13) "127.0.0.1"
      ["SERVER_PORT"]=>
      string(2) "80"
      ["REMOTE_ADDR"]=>
      string(11) "127.0.0.1"
      ["DOCUMENT_ROOT"]=>
      string(51) "/Users/brett/www/"
      ["SERVER_ADMIN"]=>
      string(14) "[email protected]"
      ["SCRIPT_FILENAME"]=>
      string(79) "/Users/brett/www/demo_brett/listing/index.php"
      ["REMOTE_PORT"]=>
      string(5) "63737"
      ["REDIRECT_URL"]=>
      string(31) "/demo_brett/listing/hello-world"
      ["GATEWAY_INTERFACE"]=>
      string(7) "CGI/1.1"
      ["SERVER_PROTOCOL"]=>
      string(8) "HTTP/1.1"
      ["REQUEST_METHOD"]=>
      string(3) "GET"
      ["QUERY_STRING"]=>
      string(0) ""
      ["REQUEST_URI"]=>
      string(31) "/demo_brett/listing/hello-world"
      ["SCRIPT_NAME"]=>
      string(29) "/demo_brett/listing/index.php"
      ["PHP_SELF"]=>
      string(29) "/demo_brett/listing/index.php"
      ["REQUEST_TIME"]=>
      int(1390244324)
    }
    

    Does anyone know what may be occurring?

    Question/Help 
    opened by brettalton 14
  • RFC - HTTP Error handling

    RFC - HTTP Error handling

    Currently, the way that 404's and 405's are handled are through special respond() calls or Route instances in the collection with a special path of 404 or 405.

    Although this proves convenient, its also hidden functionality and proves to be a magical feature that isn't easily documented. Not only this, but since it takes a traditional approach of simply adding a route in the callback collection/stack, the functionality has limitations, such as having to make sure to define them in the right order and making sure the route processing loop doesn't get halted.

    In this RFC, I'm proposing that we take a new approach to letting a developer listen for certain status code errors through another means, such as a dedicated error handling method and callback stack (similar to our current onError() handlers, except JUST for HTTP status errors).

    This could be a something simple like Sinatra's solution, or another means. I'm still developing a more full idea of how this could be implemented, but before I wasted my time I wanted to see if working on a new handler for route HTTP errors was something you agreed with.

    RFC 
    opened by Rican7 14
  • Feature - Route Match (Dispatch) Control

    Feature - Route Match (Dispatch) Control

    This Pull Request adds a new feature, allowing a developer to take more control of how the Klein->dispatch() method works with route callbacks.

    In response to issues/PR's like #89 and #105, I thought it would be an interesting idea to explore the idea of allowing exceptions (and nice alias methods) to allow the control of the dispatch's callback execution.

    This PR adds a new DispatchHaltedException exception class with constant values aligning with its halt-style, and a $number_of_skips property to contain the number of callbacks that should be skipped.

    There are also some nice convenient methods in the Klein class, such as:

    • skipThis() - For skipping/stopping the current callback
    • skipNext($num) - For skipping/stopping the next (or $num of) callbacks
    • skipRest() - For stopping the rest of the callbacks

    I wanted to add this as a pull request, instead of immediately merging it, so that followers of the project and developers like @daveroberts and @unstoppablecarl could take a look at the approach and discuss/give feedback on the idea.

    So, please let me know what you think, so we can do this right the first time. :+1:

    Feature 
    opened by Rican7 14
  • Selectively pass parameters to callback

    Selectively pass parameters to callback

    With the current implementation, the only way to access variables like $matched or $response is to include all the proceeding parameters.

    For example, in issue #166, to use $matched the callback had to be defined like so.

    $klein->respond(function($request, $response, $service, $app, $matched) {
        if ($matched > 0) {
            // We must have matched a route earlier
        } else {
            // I guess we didn't match anything earlier, let's go home...
        }
    });
    

    This pull request allows parameters to be defined in any order and Klein will "magically" assign them.

    For instance

    $klein->respond(function($matched) {
        if ($matched > 0) {
            // We must have matched a route earlier
        } else {
            // I guess we didn't match anything earlier, let's go home...
        }    
    });
    
    opened by BaylorRae 13
  • Klein v2

    Klein v2

    Klein - Version 2

    Here we go, @chriso...

    Here it is, the Version 2 rewrite of Klein.php.

    Its been a long time coming: many commits, thousands of lines, and a lot of unit tests. :P

    The biggest differences are:

    • The modern OOP pattern to the code
    • The PSR-2 compliance
    • The PSR-0 autoloader compatibility
    • The more modular codebase
    • The heavily documented code (with PHPdoc style documentation)
    • The dependency injection approach to the different Request, Response, and Service handling classes
    • The more testable approach to also injecting the request data that is normally contained in the super-globals (your idea!)
    • The namespaced, contained code that keeps the global scope clean and relies less on static variables
    • The use of custom exceptions that extend the SPL (standard library) for a more consistent exception handling pattern
    • The unit tested and "integration tested" code
    • The Symfony inspired classes that have no external dependencies

    The routing algorithm itself has not been changed. Klein 2's efficiency therefore hasn't been reduced thanks to PHP's handling of objects (they're inexpensive), and the easily APC-cacheable classes (that depend less on the super-globals).

    The code was designed to satisfy more of the "Laws of Demeter" and the "Single Responsibility Principle", in that each class handles its own objects and simply interfaces with its related classes without ever directly operating on each other's properties (most of the properties are behind getters/setters).

    The use of Klein has also not changed much, other than the lack of the ability to call Klein's methods from anywhere (since the scope isn't global). This is its strength, though, as it allows multiple instances of Klein (great for testing) and a less dirty global scope with less of a chance to accidentally change Klein's intended behavior at runtime.

    Anyway

    Let me know what you think! You won't hurt my feelings if you don't like something. :tongue: Merging this code into master isn't nearly as scary of an idea now that we have well tagged versions. And after a merge, the code can be tagged as a major version (2.0.0), so that dependency managers won't automatically update!

    opened by Rican7 12
  • Feature - HTTP Error Handling

    Feature - HTTP Error Handling

    This PR introduces a new feature while fixing a known bug.

    The new feature is a more reliable and understandable way of HTTP error handling. It was implemented to satisfy the spec in the discussion made in the RFC. Users of the library may now register HTTP error handlers much like they're used to with generic exceptions and can easily handle errors thrown anywhere in their applications.

    The old way of registering 404 and 405 HTTP error handlers is still available (through special route paths), but may be removed in a future version. They're simply around for backwards compatibility.

    Also, this PR introduces a new HttpException (and interface), for easily causing the HTTP error handler to be invoked while stopping the flow of the current controller.

    Finally, a lot of behavioral tests were added since some of the bugs that this fixed weren't previously known.

    I'm especially curious as to your thoughts, @chriso, @gbouthenot, and @drallen1.

    opened by Rican7 11
  • call $callback via call_user_func

    call $callback via call_user_func

    I needed the ability to route to HTTPResource::GET, HTTPResource::PUT etc. so i changed the way klein calls the route handlers.

    By using call_user_func, any callable will be accepted as valid.

    Should be fully backwards compatible. Also added a test to ensure it actually works.

    opened by tbug 11
  • HMVC

    HMVC

    First of all great framework :) Quick question, would it be easy to call a response method (route) internally i.e. you would get the exact same data returned as if you visited the url in your browser but you can call it within the system like $response= visit('som/url/in/your/app'); maybe even chain a header on there. I'll be taking a look at your code over the weekend when I have more free time :)

    Many thanks,

    CJ

    opened by cj 11
  • Am I implementing $router->respond() properly?

    Am I implementing $router->respond() properly?

    Hello,

    I am following a no-framework tutorial and am using Klein as the router.

    In my bootstrap.php file:

    $router = new \Klein\Klein();
    $routes = include('Routes.php');
    foreach ($routes as $route) {
        $router->respond($route[0], $route[1], $route[2]);
    }
    

    In my Routes.php file: return [ ['GET', '/hello-world', function () { echo 'Hello World'; }], ['GET', '/another-route', function () { echo 'This works too'; }], ['GET', '/', ['Main\Controllers\HomeController', 'show']], ];

    This works, but it's calling HomeController::show() as a static function. I guess that makes sense, but then how do I use __construct() in my HomeController to setup the class?

    Question/Help 
    opened by funkytaco 10
  • Added option to have empty paths

    Added option to have empty paths

    For example, this code:

    $klein->with('/users/', function () use ($klein) {
        $klein->respond('GET', '', function () {
            echo 'Hello World';
        });
    });
    

    Responds to the given the path /users/, as it should, but throws an error:

    Notice: Uninitialized string offset: 0 in /klein/klein/src/Klein/RouteFactory.php on line 84
    

    This PR fixes that bug and allows for paths to be empty. Please note that providing null as an argument for the path creates a catch-all route so this is not an option.

    Bug Feature May Not Implement 
    opened by gabrielbull 10
  • Achieve return value instead of echoing

    Achieve return value instead of echoing

    Hi,

    might be a stupid question to you... but, can i somehow receive the return value of the callbacks? see example below.

    i noticed, that everything is echoed and $return is null...

    $klein = new \Klein\Klein();
    $klein->with("/admin", function() use($klein) {
        return $klein->respond(array('GET', 'POST'), '/[test:action]', function ($request) {
                    return $request->action;
        });
    
    });
    $klein->respond(array('GET', 'POST'), '/[:action]', function ($request) {
                return $request->action;
    });
    
    $return = $klein->dispatch();
    

    am i to buffer it?

    Question/Help 
    opened by sweiguny 10
  • Second Subdirectory Routing Not Working

    Second Subdirectory Routing Not Working

    I've successfully set up Klein in subdomain of my site cad.website.xyz/path/to/routing/. In there I have my .htaccess file which contains the following:

    Options -MultiViews
    
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
    

    Along with the following PHP script at the top of my index.php:

    $base  = dirname($_SERVER['PHP_SELF']);
    if(ltrim($base, '/')){$_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], strlen($base));}
    

    This config works for that portion of my site. Using the same .htaccess config with the PHP script atop my index file allows Klein to work with the respond all response, but anything else throws a 404 error. I'm not sure if it would matter much but I've tried removing the existing .htaccess file from the initial subdomain to see if it was conflicting with the one I'm attempting to set up, but it didn't change anything.

    Any ideas?

    opened by MASRPNathan-G 0
  • Add more better regex's

    Add more better regex's

    Not an issue but a feature request.

    Add more flexible regex like: [:name]{length} or something like that Add basic regex support into [:] regex like [a-z:name]

    Sorry for my bad english

    opened by ay0ks 0
  • Access pathname()">

    Access "pathname()" method in view using $this->pathname()

    This is not a bug this is the helping material

    Paste this piece of code in

    klein class folder > src > klein

    public function pathname(Request $request = null) { $uri = $this->request->uri(); // Strip the query string from the URI $uri = strstr($uri, '?', true) ?: $uri; return $uri; }

    opened by altafhpatel 0
  • is this package still maintained?

    is this package still maintained?

    I was using this and just wondering if the package is still maintained. I see a number of issues and PRs but no activity. Does the package need a new owner?

    opened by dwenaus 4
  • Central callback/hook before route response is called

    Central callback/hook before route response is called

    Each of my routes start with the locale.

    $klein->respond( '/', function ($request, $response, $service, $app) {
        $response->redirect( '/en/' );
    });
    $klein->with('/[*:locale]', function () use ($klein) {
        $klein->respond( 'GET', '/', [HomeController::class, 'get'] );
        $klein->respond( 'GET', '/foo', [FooController::class, 'get']);
    });
    

    For rendering i used twig and all information for twig is stored inside a service array called context.

    $klein->respond( function ( $request, $response, $service, $app ) {
        $app->context = [
            'pageTitle' => 'Demo Site',
    	'locale' => $request->locale
        ];
    });
    

    But at time of context initialization is locale null. When my callable of the respond-function is called only then is $request-locale available. I tried different approaches but everytime is locale not available.

    opened by felixWackernagel 1
Releases(v2.1.2)
  • v2.1.2(Feb 1, 2017)

    Features

    • PHP 7 Throwables will now properly be caught and sent up the exception handling chain.
    • Expanded compatibility with other exception types in some method signatures

    Bug fixes

    • Broad exception catch statements will now properly handle PHP 7 Throwable types

    Upgrading

    Interface Changes

    • The RoutePathCompilationException::createFromRoute() method signature has changed to allow both Exception and Throwable types with dual support for PHP 5 and PHP 7
    • The 4th parameter to the callbacks supported by Klein#onError will now be able to receive Throwable types under PHP 7
    Source code(tar.gz)
    Source code(zip)
  • v2.1.1(Nov 1, 2016)

    Features

    • Header keys are now normalized, by default, to their canonical MIME format for consistency
    • Header key normalization is now customizable
    • Internal callback data-structures were moved from arrays to more appropriate SplQueue and SplStack instances, providing performance and memory footprint improvements
    • The PHPUnit version used for tests has been updated for HHVM compatibility
    • PHP 7.0 and HHVM compatibility!

    Bug fixes

    • A few internal property/attribute names have been updated for consistency
    • An iteration bug effecting tests run under certain HHVM runtime versions has been fixed
    • The README document has been updated to fix a few errors
    • The file() method in the Response class has been updated to fix an issue found when run under PHP-FPM
    • The file() method in the Response class will no longer send the Content-Length header when the response has been chunked, to comply with the HTTP requirements defined in RFC 2616
    • References to the old https://github.com/chriso/klein.php repository URL have been updated to the new repository URL home of Klein: https://github.com/klein/klein.php
    • Tests were updated to pass under an expanded number of PHP runtime versions and configurations
    • A potential output buffer stack miss-handling in the dispatch process has been fixed

    Upgrading

    Deprecations

    • The HeaderDataCollection::normalizeName() method has been deprecated in favor of using new normalization options (via constant switches) and other more specific methods on the same class

    Interface Changes

    • Three of the Klein internal callback attributes have changed both name and data structure. These attributes are protected, so the effect will only be felt by users that have extended and/or overwritten Klein's internal behaviors. The following changes were made:
      • Klein#errorCallbacks was renamed to Klein#error_callbacks and it's array data-structure was changed to use an SplStack
      • Klein#httpErrorCallbacks was renamed to Klein#http_error_callbacks and it's array data-structure was changed to use an SplStack
      • Klein#afterFilterCallbacks was renamed to Klein#after_filter_callbacks and it's array data-structure was changed to use an SplQueue
    • Validator#defaultAdded was renamed to Validator#default_added
    Source code(tar.gz)
    Source code(zip)
  • v2.1.0(Nov 7, 2014)

    Features

    • New exception and helper methods to help control the dispatch flow
    • New abort() method to allow stopping the routing process and returning a response code
    • Routes are now instances of a new Route class, instead of just being a set of meta properties and a callback
    • Routes are now stored in a RouteCollection class, which extends the DataCollection class
    • New keys() and clear() methods for the DataCollection class
    • Added the capability of reverse routing!
    • Now allowing for route callbacks to change the response object by returning a new ApiResponse instance
    • New "slug" type for route param matching
    • New isEmpty() and cloneEmpty() methods for the DataCollection class
    • The $matched route callback parameter is now an instance of a RouteCollection, instead of just an integer
    • Route callbacks are now passed the Klein instance for easier closure/class-scope use
    • Regular expression routing is now more accurate and will match more special characters in a similar way to Sinatra
    • Routes are now built with a dependency injected AbstractRouteFactory instance, allowing the building of routes to be customized more easily
    • New options() and head() alias methods for matching OPTIONS and HEAD requests respectively
    • The Response class has been abstracted into an AbstractResponse and a separate Response class for cleaner 3rd-party extension
    • New "after dispatch" callbacks can be registered for firing a series of callbacks after the dispatch loop has completed
    • New patch() alias method for matching PATCH requests
    • New HTTP error handling via exceptions and callback registration for a more direct (and less magical) API for controlling HTTP errors
    • The escape() method in the ServiceProvider class now allows for the passing of entity escaping flags
    • Route regular expressions are now validated and provide helpful errors upon a validation failure
    • Routes can now contain an empty string path
    • The composer autoloader is now compatible with the PSR-4 standard.
    • Regular expression compilation performance has been improved
    • 100% Code Coverage

    Bug fixes

    • The README document has been updated to fix a few typos and inconsistencies
    • Route params are now properly URL decoded
    • 404/405 routes now properly set the appropriate status code automatically
    • Silencing the locked response exceptions as the behavior is designed to be transparent/automatic
    • Allow route callables to be an array suitable for call_user_func() callable behavior
    • More proper handling for 404's that also call the 404 error handlers
    • The file() and json() methods in the Response class no longer override system-configured time processing limits
    • Now checking if the output buffer is open before attempting to close it
    • The methods matched counter ($methods_matched) is now much more accurate, not counting methods that shouldn't have been considered matches
    • Various PHPdoc inaccuracies and inconsistencies have been fixed
    • Regular expressions are now quoted during compilation in a much safer manner
    • The PHPdoc tags have been updated to use the more modern syntax

    Upgrading

    Deprecations

    • Handling 404 and 405 errors with a specially registered route callback is now deprecated. It's now suggested to use Klein's new onHttpError() method instead.
    • Autoloading the library with Composer no longer utilizes the PSR-0 spec. The composer autoloader now uses PSR-4.

    Interface Changes

    • Some of the route callback params have changed. This will effect any route definitions with callbacks using the more advanced parameters.
      • The old params were (in order):
        • Request $request
        • Response $response
        • Service $service
        • App $app
        • int $matched
        • array $methods_matched
      • The new params are (in order):
        • Request $request
        • Response $response
        • Service $service
        • App $app
        • Klein $klein
        • RouteCollection $matched
        • array $methods_matched
    • Non-match routes (routes that are wildcard and shouldn't consider as "matches") will no longer be considered as part of the "methods matched" array, since they aren't supposed to be matches in the first place
      • This may have implications for users that have created "match-all" OPTIONS method routes, as the OPTIONS method will no longer be considered a match.
      • If you'd like to conserve the old match behavior, you can simply mark the route as one that should be counted as a match with $route->setCountMatch(true)
    Source code(tar.gz)
    Source code(zip)
Owner
null
PHPRouter is an easy-to-use, fast, and flexible PHP router package with express-style routing.

PHP-Router is a modern, fast, and adaptable composer package that provides express-style routing in PHP without a framework.

Ayodeji O. 4 Oct 20, 2022
PHP Router class - A simple Rails inspired PHP router class.

PHP Router class A simple Rails inspired PHP router class. Usage of different HTTP Methods REST / Resourceful routing Reversed routing using named rou

Danny van Kooten 565 Jan 8, 2023
Fast request router for PHP

FastRoute - Fast request router for PHP This library provides a fast implementation of a regular expression based router. Blog post explaining how the

Nikita Popov 4.7k Dec 23, 2022
Pux is a fast PHP Router and includes out-of-box controller tools

Pux Pux is a faster PHP router, it also includes out-of-box controller helpers. 2.0.x Branch Build Status (This branch is under development) Benchmark

Yo-An Lin 1.3k Dec 21, 2022
:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

Sunrise // PHP 151 Jan 5, 2023
PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects.

PhpRouter PhpRouter is a powerful, lightweight, and very fast HTTP URL router for PHP projects. Some of the provided features: Route parameters Predef

Milad Rahimi 152 Dec 28, 2022
Flight routing is a simple, fast PHP router that is easy to get integrated with other routers.

The PHP HTTP Flight Router divineniiquaye/flight-routing is a HTTP router for PHP 7.1+ based on PSR-7 and PSR-15 with support for annotations, created

Divine Niiquaye Ibok 16 Nov 1, 2022
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project.

Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expand-ability in mind.

Simon Sessingø 472 Jan 4, 2023
A lightweight and fast router for PHP

Piko Router A lightweight and blazing fast router (see benchmarks) using a radix trie to store dynamic routes. This router maps routes to user defined

Piko framework 62 Dec 27, 2022
A web router implementation for PHP.

Aura.Router Powerful, flexible web routing for PSR-7 requests. Installation and Autoloading This package is installable and PSR-4 autoloadable via Com

Aura for PHP 469 Jan 1, 2023
:bird: Simple PHP router

Macaw Macaw is a simple, open source PHP router. It's super small (~150 LOC), fast, and has some great annotated source code. This class allows you to

Noah Buscher 895 Dec 21, 2022
Toro is a PHP router for developing RESTful web applications and APIs.

Toro Toro is a PHP router for developing RESTful web applications and APIs. It is designed for minimalists who want to get work done. Quick Links Offi

Kunal Anand 1.2k Dec 27, 2022
A lightweight and simple object oriented PHP Router

bramus/router A lightweight and simple object oriented PHP Router. Built by Bram(us) Van Damme (https://www.bram.us) and Contributors Features Support

Bramus! 935 Jan 1, 2023
A simple PHP Router

Panda Router Description the panda-router is a small alternative PHP router that can be used for small projects. With this router you can use differen

Jan Behrens 1 Dec 27, 2021
Thruway - an open source client and router implementation of WAMP (Web Application Messaging Protocol), for PHP.

PHP Client and Router Library for Autobahn and WAMP (Web Application Messaging Protocol) for Real-Time Application Messaging

Voryx 661 Nov 14, 2022
A router for Amp's HTTP Server.

http-server-router This package provides a routing RequestHandler for Amp's HTTP server based on the request URI and method based on FastRoute. Instal

AMPHP 34 Dec 19, 2022
OpenAPI (Swagger) Specification Support for Sunrise Router (and not only)

OpenAPI (Swagger) Specification Support for Sunrise Router Important to understanding OpenAPI Specification Installation composer require 'sunrise/htt

Sunrise // PHP 3 Feb 12, 2022
A lightweight and very basic PHP router.

Katya A lightweight PHP router Configuration Para servidor Apache, en el directorio del proyecto crea y edita un archivo .htaccess con lo siguiente: <

Luis Rodríguez 0 Apr 4, 2022
A PHP Router Package

Router A PHP Router Package Basic Concepts A router package is a utility that, once all http requests are redirected to an entry point, can configure

null 0 Aug 26, 2022