Coder Social home page Coder Social logo

riverside / php-express Goto Github PK

View Code? Open in Web Editor NEW
25.0 3.0 9.0 588 KB

:horse: PHP micro-framework inspired by Express.js

Home Page: https://riverside.github.io/php-express/

License: MIT License

PHP 100.00%
php php-framework php-router router express application web-framework expressjs viewcontroller view controller webapp micro-framework

php-express's People

Contributors

jameswilson avatar riverside avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

php-express's Issues

Robustify how Request->scheme and Request->secure are calculated

Problem statement

Using $_SERVER['REQUEST_SCHEME'] === 'https' is not always trustworthy. First of all, it's only available in Apache >=2.4. It doesn't even exist in nginx, IIS, lighttpd and other web servers.

Proposed resolution

Create a protected method Request::getRequestScheme() that can properly calculate whether http or https was used. Use this function to set Request->scheme and Request->secure member variables.

   protected static function getRequestScheme() : bool
    {
        // Modern servers will have the HTTPS header set to 'on' or '1'.
        if (
            isset($_SERVER['HTTPS'])
            && (
                strtolower($_SERVER['HTTPS']) == 'on'
                || $_SERVER['HTTPS'] == 1
            )
        ) {
            return 'https';
        }
        // Some reverse proxies and load balencers will have the
        // HTTP_X_FORWARDED_PROTO header set to 'https'.
        else if (
            isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
            && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https'
        ) {

            return 'https';
        }
        // Other reverse proxies and load balencers will have the
        // HTTP_FRONT_END_HTTPS headers set to 'on' or '1'.
        else if (
            isset($_SERVER['HTTP_FRONT_END_HTTPS'])
            && (
                strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) == 'on'
                || $_SERVER['HTTP_FRONT_END_HTTPS'] == 1
            )
        ) {
            return 'https';
        }
        // Apache server may have the REQUEST_SCHEME header available.
        else if (
            isset($_SERVER['REQUEST_SCHEME'])
            && strtolower($_SERVER['REQUEST_SCHEME']) == 'https'
        ) {
            return 'https';
        }
        // If all else fails, try the standard SSL server port '443'.
        else if (
            isset($_SERVER['SERVER_PORT'])
            && intval($_SERVER['SERVER_PORT']) === 443
        ) {
            return 'https';
        }
        return 'http';
    }

error 404 method (notFound)

has php-express default error (notFound) method to show 404 error page ?
any hints to write this ? how to do it ?

Support other template engines

Thanks for your work on this project. I'm planning on using it to put together a simple HTMX example app.

It would be interesting to add in optional support for the Twig template engine. Regardless of anyone's stance on Symfony, it is a fantastic template language and doesn't even require the entire Symfony package ecosystem to use it standalone.

Something like this would be cool:

composer require twig/twig
$app = new \PhpExpress\Application();
$app->set('views', 'path/to/views');
$app->set('twig_options', [
  'cache' => 'path/to/twig_cache',

  // Uncomment for local development only.
  // 'debug' => true,
  // 'auto_reload' => true,
]);

$app->get('/', function ($req, $res) {
    $res->render('base', [
        'title' => 'Home page',
        'message' => 'This is the home page',
    ]);
});

$app->get('about', function ($req, $res) {
    $res->render('about', [
        'title' => 'About page',
        'message' => 'This is the about page',
    ]);
});

Then in template folder we have:

views/base.twig:

<html>
<head>
<title>{{ title }}</title>
</head>
<body>
{% block header %}
<h1>{{ title }}</h1>
{% endblock %}
{% block content %}
<div>{{ content }}</div>
<a href="/about">About</a>
{% endblock %}
</body>
</html>

views/about.html.twig:

{% extends "base.twig" %}
{% block content %}
<div>{{ content }}</div>
<a href="/">Home</a>
{% endblock %}

In Application::render() we can hand down local variables into the Twig template if a matching view filename with extension .twig is found.

Then we can check if Twig FilesystemLoader class exists. The following is working for me:

    public function render(string $view, array $locals=null): void
    {
        if ($locals)
        {
            extract($locals);
        }
        $views_dir = $this->set("views");
        $layout = sprintf("%s/%s.php", $views_dir, $view);
        $_twig = sprintf("%s/%s.twig", $views_dir, $view);
        $_template = sprintf("%s/%s.php", $views_dir, $this->set("template"));
        if (is_file($_template))
        {
            include $_template;
        } elseif (is_file($_twig)) {
            if (!class_exists('\Twig\Loader\FilesystemLoader')) {
                $filepath = realpath($_twig);
                throw new \Exception("Please `composer require twig/twig` to proceed. A Twig template '{$filepath}' was found but the Twig template engine could not be loaded");
            }
            $loader = new \Twig\Loader\FilesystemLoader($views_dir);
            $twig = new \Twig\Environment($loader, $this->set("twig_options"));
            echo $twig->render(sprintf('%s.twig', $view), $locals);
        } else {
            include $layout;
        }
    }

Pass a static function to Application->use

I have a class named Middleware and all the middlewares are declared as static functions in that class. How can I pass any of the function to use it as middleware. I tried following code but it does not work.

$app->use(Middlewares::setDeveloperHeader); // Here setDeveloperHeader is a public static function of class Middlewares

Any help would be appreciated.

Populate request data payload for RESTful HTTP methods like PUT, PATCH, DELETE

Problem statement

Support for PUT, PATCH, DELETE verbs exist (Eg \PhpExpress\Application::put() and \PhpExpress\Application::patch()) but are of limited use because there is no payload data populated into in the Request object.

Steps to reproduce

  1. Create an index.php with the following:
$app = new \PhpExpress\Application();

$testVerbs = function($req, $res) {
  echo "method:" . var_export($req->method, TRUE) . PHP_EOL;
  echo "content-type:" . var_export($req->get("content-type"), TRUE) . PHP_EOL;
  echo "params:" . var_export($req->params, TRUE) . PHP_EOL;
  echo "body:" . var_export($req->body, TRUE) . PHP_EOL;
  $res->end();
};

$app->get('contact/:id', $testVerbs);
$app->post('contact/:id', $testVerbs);
$app->put('contact/:id', $testVerbs);
$app->patch('contact/:id', $testVerbs);
  1. Start the server and make several requests with various HTTP request methods via curl.
curl -X GET -d argument=value -d argument2=value2 http://localhost:8000/contact/1
curl -X POST -d argument=value -d argument2=value2 http://localhost:8000/contact/1
curl -X PUT -d argument=value -d argument2=value2 http://localhost:8000/contact/1
curl -X PATCH -d argument=value -d argument2=value2 http://localhost:8000/contact/1
  1. The expected result in all cases should be:
content-type:'application/x-www-form-urlencoded'
params:array (
  'id' => '1',
)
body:array (
  'argument' => 'value',
  'argument2' => 'value2',
)
  1. But the actual result varies by HTTP Request Method.

    Only the POST request type matches the expected result above with a populated $req->body

    All others show an empty body:

content-type:'application/x-www-form-urlencoded'
params:array (
  'id' => '1',
)
body:array (
)

Proposed Resolution

Populate the $req->body with the payload from PHP's Standard Input stream via PHP's parse_str() when $_POST is empty. For simplicity, since the $req->body is just a pointer to $_POST superglobal, we can parse the data into $_POST directly which is guaranteed to be empty for any HTTP request method other than POST.

parse_str(file_get_contents('php://input'), $_POST);

Forms can be submitted with different encoding types in the Content-Type header. application/x-www-form-urlencoded is the default, but multipart/form-data could also be accepted. However handling files uploaded with multipart/form-data encoding via HTTP PUT is not supported in PHP <8.4, therefore the parsing has to be done in different ways.

Full and future-proof solution, for when php 8.4 drops in November 2024:

        // Make data payloads available in $this->body for other HTTP verbs.
        if (
            $this->method !== 'POST'
            && $this->is('application/x-www-form-urlencoded')
        ) {
            parse_str(file_get_contents('php://input'), $_POST);
        }

        // Add support for multipart/form-data parsing for other HTTP verbs in PHP >=8.4.
        if (
            $this->method !== 'POST'
            && $this->is('multipart/form-data')
            && function_exists('request_parse_body')
        ) {
            [$_POST, $_FILES] = call_user_func('request_parse_body');
        }

[Question] How to pass Custom Parameters inside URL

Hi!

I have read code and documentation provided but I couldn't find a way to pass parameters like we do in express.js. Yeah I did find a way to pass "hash" and numeric "id" but not a custom parameter.

$app.get('/user/:username', function ($req, $res) {
   $res->send($req->params['username']);
}

Is there a way to do that in php-express?

No clear cookies

At the line 207, set $domain = isset($options['domain']) ? $options['domain'] : ""; to $domain = isset($options['domain']) ? $options['domain'] : "/"; for clear cookies correctly

Query param _path_

is it possible to suggest $_SERVER['REQUEST_URI'] instead of $_GET['_path_'] ?

Not Found Error on All routes excpet "/"

It is giving not found error on all pages except "/" route. And I have tested it on basic example. Nothing Complex.

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . "/vendor/autoload.php";
use \PhpExpress\Application;

$app = new Application();

$app->get("/", function ($req, $res) {
    $res->send("Root");
});

$app->get("/test", function ($req, $res) {
    $res->send("Test");
});

$app->run();

PHP Version: 7.4

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.