TurtlePHP is a PHP MVC-based framework. I use the word framework loosely, as it contains very few binding features, and uses a minimalistic approach to make development within it easy and natural (for PHP developers, that is).
- Data access ought to be managed through a corresponding object's model
- Output of a request ought to contained within a view
- Business/middleware logic ought to be managed through a controller (and through a controller's action/method)
- Output buffer can be passed through a single, or series of, closures, to manipulate output before it is sent to the client
- Useful for writing framework-wide plugins to (eg. clean output, inject security headers/content)
- Programmatic (eg. not 404) errors are routed to an internal file which presents a friendly user interface
- Routes can be added to the application dynamically (eg. by a plugin)
- Useful in the creation of plugins that provide RESTful functionality
- Include the
alternatives
property to serve the same controller/action from different routes (string
orarray
)
- TurltePHP's core directory contains just 6 files, and is cumulatively less than 20kb
- While not having been benchmarked, it's simplicity and minimalism allows for unencumbered application development
- Logging Allows for the modification of PHP's default error logging
- Roles Provides a standardized way to differentiate between different codebase environments (eg. local, development, staging, production)
- Config Provides a standardized approach for storing and retrieving an application's configuration settings
- Performance Analyzes a response that is ready for flushing, determines it's processing duration and memory usage, and returns them through custom response-headers
- Template Passes buffer through a templating-engine to convert custom-tags programatically. Has the PHP-Template library as a requirement
- Error
Still under development, this plugin modifies the framework's default error handling behaviour (which is to render the output of the included
error.inc.php
file), by outputting a friendly UI which includes the error message, file, line number, and code snippet. Stack trace will be added soon as well.
The following is what's required to get your application up and
running with a Hello World example ready to go.
Add a virtual host for your site, as follows:
<VirtualHost *:80>
ServerName hostname.com
ServerAlias www.hostname.com
DocumentRoot /var/www/directory
# turtle routing
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/application/webroot%{REQUEST_URI} !-f
RewriteCond %{DOCUMENT_ROOT}/application/webroot%{REQUEST_URI} !-d
RewriteRule ^(.*)$ %{DOCUMENT_ROOT}/core/index.php [L,QSA]
RewriteRule (.*) %{DOCUMENT_ROOT}/application/webroot$1 [L,QSA]
</VirtualHost>
While not a core requirement for setting up a TurtlePHP project, sessions are ubiquitous with web requests nowadays. I use my PHP-SecureSessions library for this, and it works pretty well.
If your infrastructure is distributed, the SMSession
class ought to be used,
but requires memcached to be installed on all servers/instances.
You may find it useful to extend the default controller for your application. A sample of such a case would be as follows:
<?php
/**
* AppController class.
*
* @extends \Turtle\Controller
*/
class AppController extends \Turtle\Controller
{
/**
* prepare
*
* @access public
* @return bool
*/
public function prepare(): bool
{
$authenticated = false;
if (
isset($_SESSION['authenticated'])
&& $_SESSION['authenticated'] === true
) {
$authenticated = true;
}
$this->_pass('authenticated', $authenticated);
$response = parent::prepare();
return $response;
}
}
The above AppController
class extends the default Controller
class
(specified through the Turtle
namespace), and defines one method: prepare
.
This method is processed before a child controller's action during a request flow, and allows you to include logic that should be processed application-wide.
A sample implementation of this application-level controller:
<?php
// dependency
require_once 'App.class.php';
/**
* CommonController
*
* Common requests that most applications ought to facilitate.
*
* @extends AppController
* @final
*/
final class CommonController extends AppController
{
}
You are now able to make sub-requests from within a controller using the following format:
<?php
// grab new response
$subrequest = new \Turtle\Request('/path/?including=params');
$subrequest->route();
$subrequest->generate();
$response = $subrequest->getResponse();
Want to have this sub-request run through the templating plugin?
<?php
// grab new response
$subrequest = new \Turtle\Request('/path/?including=params');
new \Plugin\Template($subrequest);
$subrequest->route();
$subrequest->generate();
$response = $subrequest->getResponse();
Note that sub-requests will have access to all data that are meant to be passed
to a parent-request's view using the _pass
method.
That means, that if a variable is passed before the sub-request is initiated, the sub-request will have access to it.
Additionally, any variables that the sub-request itself passes will be made available to the view that the originating-request/controller gets rendered under.
It's curious (yet most of the time, irrelevant) functionality that is required
to keep the flow for sub-requests clean, and prevent any unnecessary calls to
the framework native <prepare>
method.
From within a controller action/method, variables can be passed as follows:
$this->_pass('name', 'Oliver Nassar');
$this->_pass('page.title', 'Webpage Title');
$this->_pass('page.description', 'Sample description');
Respectively, the following variables are made available in the view:
$name = 'Oliver Nassar';
$page = array(
'title' => 'Webpage Title',
'description' => 'Sample description'
);
Note that the period prompts the storage-system to store the value in a child-array, if it hasn't yet been defined.
Hooks can be added to the Application
By adding a hook (by passing a name
and valid callback array to addHook
), you are registering it with the application to be used application-wide
One example that is in use immediately is an error hook
Any error hooks added will be run after an error has occured through the fundamental proxy
function that is part of this framework
The goal with hooks is to give framework-level access to non-core pieces of code (eg. controllers, plugins, modules, etc.)
To have a WordPress install live at eg. /blog/
, here's what's needed:
-
From within
webroot
directory, install it from source (instructions):wget http://wordpress.org/latest.tar.gz tar -xzvf latest.tar.gz rm latest.tar.gz mv wordpress/ blog/
-
Create .htaccess file:
bash -c "cat >> blog/.htaccess" <<EOF DirectoryIndex index.php EOF
-
Add the following to the
VirtualHost
entry for the site, before the Turtle routing (eg. http://i.imgur.com/pUlnjzU.png):RewriteCond %{DOCUMENT_ROOT}/application/webroot%{REQUEST_URI} !-f RewriteCond %{DOCUMENT_ROOT}/application/webroot%{REQUEST_URI} !-d RewriteRule ^/blog/.* %{DOCUMENT_ROOT}/application/webroot/blog/index.php [L]
-
Head over to domain.com/blog/ and go through install process. This will include copy/pasting config file
-
If being served via https, add the following to
wp-config.php
before therequire_once
call againstwp-settings.php
define('FORCE_SSL_ADMIN', true); define('FORCE_SSL_LOGIN', true); if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $_SERVER['HTTPS'] = 'on'; }
-
If being served via https, ensure WordPress General Settings have protocol set to https: https://i.imgur.com/5RkCRAo.png
-
If being served via https, ensure ssl traffic is forwarded (eg. https://i.imgur.com/euahgGo.png)
RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [L]
-
If updates ought to be done via ssh, install SSH SFTP Updater Support with:
cd ./webroot/blog/wp-content/plugins/ wget https://downloads.wordpress.org/plugin/ssh-sftp-updater-support.0.7.1.zip unzip ssh-sftp-updater-support.0.7.1.zip rm ssh-sftp-updater-support.0.7.1.zip
Then header over to domain.com/blog/wp-admin/plugins.php
and activate the plugin
9) Ensure proper global wp-content
permissions (only do if you own the server):
cd ./webroot/blog
sudo chmod -R 777 wp-content/
Done