/Swiftlet

Quite possibly the smallest MVC framework you'll ever use.

Primary LanguagePHPMIT LicenseMIT

Swiftlet

Swiftlet is quite possibly the smallest MVC framework you'll ever use. And it's swift.

Licensed under the MIT license.

Buzzword compliance

✔ Micro-Framework
✔ Loosely coupled
✔ Unit tested
✔ Namespaced
✔ Pluggable
ComposerPSR-4 ✔ PHP5
✔ MVC
✔ OOP

✘ ORM

Installation

  • Clone (or download and extract) Swiftlet into a directory on your PHP supported web server.
  • Having Composer installed, run composer dump-autoload.

Getting started: controllers and views

Let's create a page. Each page consists of a controller and at least one view.

The controller does most of the work; views should be limited to simple presentation logic (loops and switches).

Controller src/HelloWorld/Controllers/Foo.php

<?php
namespace HelloWorld\Controllers;

class Foo extends \Swiftlet\Abstracts\Controller
{
	protected $title = 'Foo'; // Optional but usually desired 

	// Default action
	public function index(array $args = $args)
	{
		// Pass a variable to the view
		$this->view->helloWorld = 'Hello world!';
	}
}

Important: class names are written in CamelCase and match their filename.

View views/foo.php

<h1><?= $this->pageTitle ?></h1>

<p>
	<?= $this->helloWorld ?>
</p>

The controller can set variables directly on the view. Values are automatically made safe for use in HTML, use $this->get('variable', false) on values that should be treated as code.

You can now view the page by navigating to http://<swiftlet>/foo in your web browser!

If you get a "404 Not Found" you will need to enable rewrites in the web server configuration. Alternatively you can navigate to http://<swiftlet>?q=foo.

Swiftlet can be invoked from the command line (e.g. to run cron jobs). Simply run php public/index.php -q foo.

Routing

Notice how you can access the page at /foo by simply creating a controller named Foo. The application maps URLs to controllers, actions and arguments.

Consider this URL: /foo/bar/baz/qux

In this case foo becomes the name of the controller and view, bar the name of the action and baz and qux are arguments (accessible through the $args variable in the controller).

If the controller or action is not specified they default to index (/ will call index() on HelloWorld\Controller\Index).

Underscores in the controller name are translated to directory separators, so /foo_bar will point to src/HelloWorld/Controllers/Foo/Bar.php.

Dashes in routes are ignored; /foo-bar/baz-qux calls bazqux() in src/HelloWorld/Controllers/Foobar.php.

Custom routes

Automatic routing is convenient but more granular control is often desirable. In these cases custom routes can be defined.

A route maps a URL to an action (method).

URL segments can be replaced with a "wildcard" placeholder (a variable name prefixed with a colon). This value becomes available for use in the controller.

Consider this route: bar/:qux

Navigating to <controller>/bar/something matches this route. The value of $args['qux'] becomes something.

<?php
namespace HelloWorld\Controllers;

class Foo extends \Swiftlet\Abstracts\Controller
{
	protected $routes = array(
		'hello/world' => 'index',
		'bar/:qux'    => 'bar'
		);

	public function index(array $args = $args)
	{
		// You navigated to foo/hello/world
	}

	public function bar(array $args = $args)
	{
		// You navigated to foo/bar/<something>
		// $args['qux'] contains the second URL argument
	}
}

Actions and arguments

Actions are methods of the controller. A common example might be edit or delete:

/blog/edit/1

This will call the function edit() on Blog with 1 as an argument (the id of the blog post to edit).

Arguments can be accessed through $this->app->getArgs().

To use a different view for a specific action you may change the value of $this->view->name. The view name is a filename relative to the views directory, without the .php suffix.

Models

Let's throw a model into the mix and update the controller.

Model src/HelloWorld/Models/Foo.php

<?php
namespace HelloWorld\Models;

class Foo extends \Swiftlet\Abstracts\Model
{
	public function getHelloWorld()
	{
		return 'Hello world!';
	}
}

Controller src/HelloWorld/Controllers/Foo.php

<?php
namespace HelloWorld\Controllers;

class Foo extends \Swiftlet\Abstracts\Controller
{
	protected $title = 'Foo';

	public function index()
	{
		// Get an instance of the Example class 
		// See src/HelloWorld/Models/Example.php
		$example = \HelloWorld\Models\Example;

		$this->view->helloWorld = $example->getHelloWorld();
	}
}

A model typically represents data. This can be an entry in a database or an object such as a user.

<?php
$user = \HelloWorld\Models\User;

$user->setEmail('example@example.com');

$user->save();

Loading and saving data should almost always happen in a model. You can create as many models as you like; they aren't tied to specific controllers or views.

Plugins and hooks

Plugins implement hooks. Hooks are entry points for code that extends the application. Swiftlet has a few core hooks and additiontal ones can be registered pretty much anywhere using $this->app->registerHook($hookName, $controller, $view).

Plugin src/HelloWorld/Plugins/Foo.php

<?php
namespace HelloWorld\Plugins;

class Foo extends \Swiftlet\Abstracts\Plugin
{
	public function actionAfter()
	{
		// Overwrite our previously set "helloWorld" variable
		$this->view->helloWorld = 'Hi world!';
	}
}

This plugin implements the core actionAfter hook and changes the view variable helloWorld from our previous example to Hi world!.

Plugins don't need to be installed or activated, all files in the src/HelloWorld/Plugins/ directory are automatically included and their classes instantiated. Plugins are hooked in alphabetical order.

The core hooks are:

  • actionBefore
    Called before each action

  • actionAfter Called after each action

Libraries

Reusable components such as code to send an email or generate a thumbnail image should go in a separate library class.

<?php
$email = \HelloWorld\Libraries\Email

$email->send($to, $subject, $message);

Configuration

No configuration is needed to run Swiftlet. If you're writing a model that does require configuration, e.g. credentials to establish a database connection, you may use the application's setConfig and getConfig methods:

<?php
$this->app->setConfig('variable', 'value');

$value = $this->app->getConfig('variable');

Values can be set in config/main.php or a custom file.

Public methods

Application Swiftlet\App

  • App dispatchController()
    Determine which controller to use and run it

  • App serve()
    Serve the page

  • mixed getConfig(string $variable)
    Get a configuration value

  • App setConfig(string $variable, mixed $value)
    Set a configuration value

  • App registerHook(string $hookName, array $params)
    Register a hook

View Swiftlet\View

  • mixed get(string $variable [, bool $htmlEncode = true ])
    Get a view variable, encoded for safe use in HTML by default

  • View set(string $variable [, mixed $value ])
    Set a view variable

  • mixed get(string $variable [, bool $htmlEncode ])
    Get a view variable, pass false as the second parameter to prevent values from being HTML encoded.

  • string getRootPath()
    Absolute client-side path to the website root

  • mixed htmlEncode(mixed $value)
    Recursively make a value safe for HTML

  • mixed htmlDecode(mixed $value)
    Recursively decode a previously encoded value to be rendered as HTML

  • View render(string $path)
    Render the view