Skip to content

Commit

Permalink
improved php module
Browse files Browse the repository at this point in the history
  • Loading branch information
Romain-P committed Mar 10, 2019
1 parent 2b971e4 commit 1bdd0e6
Show file tree
Hide file tree
Showing 17 changed files with 273 additions and 30 deletions.
13 changes: 8 additions & 5 deletions sources/core/src/Http.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,18 @@ namespace http {
data += std::to_string(response.status_code) + " ";
data += response.status_message + "\n";

for (auto &it: response.headers)
data += (it.first + ": " + it.second + "\n");
bool first = true;
for (auto &it: response.headers) {
if (!first) data += "\n";
data += (it.first + ": " + it.second);
if (first) first = false;
}

data += headers_end_delimiter;
buffer.insert(buffer.end(), data.begin(), data.end());

if (!response.body.empty())
data.insert(data.end(), response.body.begin(), response.body.end());

buffer.insert(buffer.end(), data.begin(), data.end());
buffer.insert(buffer.end(), response.body.begin(), response.body.end());
}
}

Expand Down
1 change: 1 addition & 0 deletions sources/modules/php/dist/sample/content/404.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>404 - This page does not exist.</p>
2 changes: 2 additions & 0 deletions sources/modules/php/dist/sample/content/about-us.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>This is <b>about</b> page. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
2 changes: 2 additions & 0 deletions sources/modules/php/dist/sample/content/contact.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>This is <b>contact</b> page. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
2 changes: 2 additions & 0 deletions sources/modules/php/dist/sample/content/home.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>This is <b>home</b> page.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
2 changes: 2 additions & 0 deletions sources/modules/php/dist/sample/content/products.phtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>This is <b>product</b> page. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
</p>It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
26 changes: 26 additions & 0 deletions sources/modules/php/dist/sample/includes/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* Used to store website configuration information.
*
* @var string or null
*/
function config($key = '')
{
$config = [
'name' => 'Simple PHP Website',
'site_url' => '',
'pretty_uri' => false,
'nav_menu' => [
'' => 'Home',
'about-us' => 'About Us',
'products' => 'Products',
'contact' => 'Contact',
],
'template_path' => 'template',
'content_path' => 'content',
'version' => 'v3.0',
];

return isset($config[$key]) ? $config[$key] : null;
}
80 changes: 80 additions & 0 deletions sources/modules/php/dist/sample/includes/functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

/**
* Displays site name.
*/
function site_name()
{
echo config('name');
}

/**
* Displays site url provided in conig.
*/
function site_url()
{
echo config('site_url');
}

/**
* Displays site version.
*/
function site_version()
{
echo config('version');
}

/**
* Website navigation.
*/
function nav_menu($sep = ' | ')
{
$nav_menu = '';
$nav_items = config('nav_menu');
foreach ($nav_items as $uri => $name) {
$class = str_replace('page=', '', $_SERVER['QUERY_STRING']) == $uri ? ' active' : '';
$url = config('site_url') . '/' . (config('pretty_uri') || $uri == '' ? '' : '?page=') . $uri;

$nav_menu .= '<a href="' . $url . '" title="' . $name . '" class="item ' . $class . '">' . $name . '</a>' . $sep;
}

echo trim($nav_menu, $sep);
}

/**
* Displays page title. It takes the data from
* URL, it replaces the hyphens with spaces and
* it capitalizes the words.
*/
function page_title()
{
$page = isset($_GET['page']) ? htmlspecialchars($_GET['page']) : 'Home';

echo ucwords(str_replace('-', ' ', $page));
}

/**
* Displays page content. It takes the data from
* the static pages inside the pages/ directory.
* When not found, display the 404 error page.
*/
function page_content()
{
$page = isset($_GET['page']) ? $_GET['page'] : 'home';

$path = getcwd() . '/' . config('content_path') . '/' . $page . '.phtml';

if (! file_exists($path)) {
$path = getcwd() . '/' . config('content_path') . '/404.phtml';
}

echo file_get_contents($path);
}

/**
* Starts everything and displays the template.
*/
function init()
{
require config('template_path') . '/template.php';
}
12 changes: 9 additions & 3 deletions sources/modules/php/dist/sample/index.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
<?php
if (true) { echo 'hello'; }
?>
<h1>lol</h1>

// Comment these lines to hide errors
error_reporting(E_ALL);
ini_set('display_errors', 1);

require 'includes/config.php';
require 'includes/functions.php';

init();
2 changes: 2 additions & 0 deletions sources/modules/php/dist/sample/simple/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.idea
.php_cs.cache
6 changes: 6 additions & 0 deletions sources/modules/php/dist/sample/simple/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?page=$1 [L]
20 changes: 20 additions & 0 deletions sources/modules/php/dist/sample/template/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

.wrap {
max-width: 720px;
margin: 50px auto;
padding: 30px 40px;
text-align: center;
box-shadow: 0 4px 25px -4px #9da5ab;
}
article {
padding: 40px;
line-height: 150%;
text-align: left;
}

nav .item {
text-decoration: none;
}
nav .active {
border-bottom: 1px solid;
}
29 changes: 29 additions & 0 deletions sources/modules/php/dist/sample/template/template.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title><?php page_title(); ?> | <?php site_name(); ?></title>
<link href="<?php site_url(); ?>/template/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="wrap">

<header>
<h1><?php site_name(); ?></h1>
<nav class="menu">
<?php nav_menu(); ?>
</nav>
</header>

<article>
<h2><?php page_title(); ?></h2>
<?php page_content(); ?>
</article>

<footer>
<small>&copy;<?php echo date('Y'); ?> <?php site_name(); ?>.<br><?php site_version(); ?></small>
</footer>

</div>
</body>
</html>
99 changes: 82 additions & 17 deletions sources/modules/php/src/RequestHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
#include <iostream>
#include <chrono>
#include <array>
#include <boost/asio.hpp>
#include <Zia/API.hpp>
#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/filesystem.hpp>
#include <regex>

using namespace Zia::API;
Expand All @@ -14,22 +15,25 @@ using namespace boost::process;
class PhpRequestHandler : public RequestHandler {
public:
PhpRequestHandler(std::string const &phpBinaryPath, std::string const &phpWwwPath)
: RequestHandler(), _phpBinaryPath(phpBinaryPath), _phpWwwPath(phpWwwPath) {}
: RequestHandler(), _phpBinaryPath(phpBinaryPath), _phpWwwPath(phpWwwPath) {}

HookResultType onRequest(const Connection &connection, const Request &request, Response &response) override {
ipstream pipe_stream;
child proc(_phpBinaryPath, std_out > pipe_stream, buildCgiEnvironment(request));
ipstream output_stream;
opstream input_stream;
child proc(_phpBinaryPath, std_out > output_stream, std_in < input_stream, buildCgiEnvironment(request));

std::string output((std::istreambuf_iterator<char>(pipe_stream)), (std::istreambuf_iterator<char>()));
parseCgi(output, response);
if (!request.body.empty())
input_stream << &request.body[0];

std::string output((std::istreambuf_iterator<char>(output_stream)), (std::istreambuf_iterator<char>()));
parseCgi(output, request, response);
proc.wait();
return HookResult::Ok;
}

HookResultType onResponse(const Connection &connection, Response &response) override {
static const std::string not_found = "The requested URL does not exist";
switch(response.status_code) {
switch (response.status_code) {
case 404:
response.body.clear();
response.body.insert(response.body.end(), not_found.begin(), not_found.end());
Expand All @@ -49,36 +53,62 @@ class PhpRequestHandler : public RequestHandler {
}

private:
environment buildCgiEnvironment(Request const &request) {
boost::process::environment cgi_env = boost::this_process::environment();

static char const *beginPath =
static inline const auto ext_html_result = std::vector<std::string>{".html", ".htm", ".php", ".phtml", ".phtm"};
static inline char const *beginPath =
#ifdef _WIN32
".\\";
#else
"./";
#endif

cgi_env["SCRIPT_FILENAME"] = beginPath + _phpWwwPath + request.uri;
environment buildCgiEnvironment(Request const &request) {
boost::process::environment cgi_env = boost::this_process::environment();

cgi_env["SERVER_PROTOCOL"] = request.protocol;
cgi_env["REQUEST_METHOD"] = request.method;
cgi_env["GATEWAY_INTERFACE"] = "CGI/1.1";
cgi_env["REDIRECT_STATUS"] = "200";
cgi_env["DOCUMENT_ROOT"] = beginPath + _phpWwwPath;

auto it = request.headers.find("Content-Length");
if (it != request.headers.end())
cgi_env["CONTENT_LENGTH"] = it->second;

it = request.headers.find("Content-Type");
if (it != request.headers.end())
cgi_env["CONTENT_TYPE"] = it->second;

setupUri(request.uri);

auto queryIndex = _requestedUri.find('?');

if (queryIndex != std::string::npos) {
cgi_env["QUERY_STRING"] = _requestedUri.substr(queryIndex + 1, _requestedUri.size());
_requestedUri = _requestedUri.substr(0, queryIndex);
} else
cgi_env["QUERY_STRING"] = _requestedUri;

cgi_env["SCRIPT_FILENAME"] = beginPath + _phpWwwPath + _requestedUri;
return cgi_env;
}

void parseCgi(std::string &data, Response &response) {
void parseCgi(std::string &data, Request const &request, Response &response) {
static const std::regex regex_status("([0-9]+)\\s+(.+)");
static const std::regex regex_header("([^:\n]+):\\s?(.+)");
static const std::regex regex_header("(?:\n)?([^:\n]+):\\s?(.+)");
static const std::string headers_end = "\r\n\r\n";

auto body_index = data.find(headers_end);
std::string headers = data.substr(0, body_index);
std::string body = data.substr(body_index + headers_end.size(), data.size());

std::smatch matcher;
while (std::regex_search(data, matcher, regex_header)) {
while (std::regex_search(headers, matcher, regex_header)) {
response.headers[matcher[1]] = matcher[2];
data = matcher.suffix();
headers = matcher.suffix();
}

setResponseHeaders(response);

auto it = response.headers.find("Status");
if (it != response.headers.end()) {
auto status = it->second;
Expand All @@ -91,11 +121,46 @@ class PhpRequestHandler : public RequestHandler {
response.status_message = "OK";
}

std::string body = data.substr(headers_end.size());
response.body.insert(response.body.end(), body.begin(), body.end());
}

void setResponseHeaders(Response &response) {
auto ext = boost::filesystem::extension(_requestedUri);
if (ext.empty()) return;

auto it = response.headers.find("Content-type");
if (std::find(ext_html_result.begin(), ext_html_result.end(), ext) == ext_html_result.end())
response.headers.erase(it);
else {
response.headers["Content-Type"] = it->second;
response.headers.erase(it);
}

it = response.headers.find("Content-length");
if (it != response.headers.end()) {
response.headers["Content-Length"] = it->second;
response.headers.erase(it);
}
}

void setupUri(std::string const &uri) {
_requestedUri = uri;

if (uri != "/" && uri.rfind("/?", 0) != 0) return;

for (auto &ext: ext_html_result) {
if (boost::filesystem::exists(beginPath + _phpWwwPath + "/index" + ext)) {
_requestedUri = "/index" + ext;

if (uri.size() > 1)
_requestedUri += uri.substr(1);
return;
}
}
}

private:
std::string _phpBinaryPath;
std::string _phpWwwPath;
std::string _requestedUri;
};
Loading

0 comments on commit 1bdd0e6

Please sign in to comment.