A very basic Controller and Template class to kickstart a simple PHP application
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
Controller.php
Exception.php
Pdo.php
README.md
Template.php

README.md

TooBasic

A very basic Controller and Template class to kickstart a simple PHP application. It is action-driven; meaning you define methods like getIndex(), getPage($name) and postForm(). These methods get called when users navigate to /index, /page/something or POST to /form respectively.

Usage

Clone this repository (or use submodules) in the directory where you want to store your own code. Then create index.php like this:

<?php

spl_autoload_register(function($class){
	if (0 === strpos($class, 'TooBasic\\'))
		require(__DIR__ .'/'. str_replace('\\', '/', $class) .'.php');
});

class My_First_Site extends TooBasic\Controller
{
}

My_First_Site::dispatch($_SERVER['QUERY_STRING']);

Additionally, TooBasic\Template expects a tpl directory with at least a _wrapper.php file.

The above dispatch() call works for URLs like http://example.com/MyFirstSite/?page/contact. If you want nice URLs you should send all requests to index.php. For Apache; create a .htaccess file with these contents:

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ /index.php

For nginx you'll probably need root-rights to add something like this to your server-configuration file:

location /
{
	include         fastcgi_params;
	fastcgi_param   SCRIPT_FILENAME $document_root/index.php;
	fastcgi_pass    unix:/var/run/php-fpm/php-fpm.sock;
}

Examples

A more complete usage example which demonstrates most features:

<?php

spl_autoload_register(function($class){
	if (0 === strpos($class, 'TooBasic\\'))
		require(__DIR__ .'/'. str_replace('\\', '/', $class) .'.php');
});

class My_First_Site extends TooBasic\Controller
{
	protected $_db;
	protected $_tpl;

	// This method gets called before the actual action-method
	protected function _construct(string $method, string $action, array $params)
	{
		$this->_db = new Pdo(/* missing parameters */);
		$this->_tpl = new TooBasic\Template;

		// Define some variables which are available in all templates
		$this->_tpl->title = 'My first site';
		$this->_tpl->time = date('r');
	}

	public function getPage($name)
	{
		$query = $this->_db->prepare("SELECT * FROM Page WHERE !deleted AND name = ?");

		if (!$query->execute(['name' => $name]))
			throw new Exception("Could not find page in database");

		// Make $this->page available in the template
		$this->_tpl->page = $query->fetch();

		// Fetch the 'page' template; it's contents are then wrapped
		// in the header and footer contained in tpl/_wrapper.php
		print $this->_tpl->get('page')->getWrapped();
	}

	// 'index' is the default action
	public function getIndex()
	{
		return $this->getPage('index');
	}

	// This gets called when a user POSTs to /form
	public function postForm()
	{
		$this->_tpl->user = htmlspecialchars($_POST['user']);

		return $this->getPage('thanks');
	}

	// You can also define a generic method which gets called when the specific action is unknown
	public function get($action)
	{
		throw new Exception('Sorry we have never heard of this "'. $action .'" you speak of');
	}

	// This action gets called when an error occurs; eg the action is unknown
	protected function _handle(Exception $e)
	{
		if (!headers_sent())
			http_response_code(500);

		// This is an alternative syntax for Template; it is equal
		// to $this->_tpl->get('error')->getWrapped() but has a
		// separate scope. Thus, it doesn't know about 'title' in
		// this example, only 'exception' is available
		TooBasic\Template::show('error', ['exception' => $e]);
	}
}

My_First_Site::dispatch();

As for templates; create tpl/_wrapper.php and tpl/page.php. The _wrapper.php could contain:

<!DOCTYPE html>
<html>
<body>
<?=$this->content?>
</body>
</html>

while the page.php could contain

<h1><?=$this->page['title']?></h1>
<p><?=$this->page['content']?></p>