/www/slight

To get this branch, use:
bzr branch http://bzr.ed.am/www/slight
1 by Tim Marston
initial commit
1
<?php
2
3
/**
4
 * Fight: an extension to Flight, in the form of useful configuration changes
5
 * and extra library code.
6
 *
7
 * @copyright Copyright (c) 2015, Tim Marston <tim@ed.am>
8
 * @licence   MIT
9
 *
10
 * FIGHT! ...brings the following changes to Flight:
11
 *
12
 *  1. Global $app object.
13
 *
14
 *  2. App directorties.
15
 *
16
 * It is expected that your app has the following layout, and class and view
17
 * paths are set up to reflect this:
18
 *
19
 *     /
20
 *     +-- index.php          (see note below)
21
 *     +-- app/
22
 *         +-- controllers/
23
 *         +-- models/
24
 *         +-- views/
25
 *
26
 * Note that index.php MUST go in the top-level directory, or autoloading
27
 * breaks.  If you want to keep it in the app/ directory, add a shim index.php
28
 * that just requires app/bootstrap.php (or whatever you want to call it).
29
 *
30
 *  3. The route() method now accepts a class name and routes to class methods.
31
 *
32
 * The flight route() method now accepts a class name in an array (so, like a
33
 * callable, except with no method specified) which indicates a class in which
34
 * the part of the URL beyond the route specified should be looked up as a
35
 * method and additional URL parts beyond those represented by the name of the
36
 * method passed as arguments to the method.
37
 *
38
 * For example, you could configure routing to a class's methods like this:
39
 *
40
 *     $app->route( '/some/path', array( 'MyClass' ) );
41
 *
42
 * Then, for example, /some/path/list will route to MyClass::action_list(),
43
 * assuming it exists.
44
 *
45
 * If MyClass::default_action() exists, then /some/path will route to it.  If
46
 * you don't require default functionality, you might still want to use this to
47
 * call some other action method.
48
 *
49
 * If other URL parts are specified beyond the name of the matched method, for
50
 * example /some/path/list/1/2, then these are passed as arguments to the
51
 * action.  You would define the action method as follows:
52
 *
53
 *     public static function action_list( $foo = 0, $bar = 0 )
54
 *
55
 * NOTE: the use of default values for arguments is REQUIRED, or a PHP error
56
 * would occur where if they were missing in the HTTP request.
57
 *
58
 * You can also specify either "special cases" of actions, or actions for a
59
 * lower level of the URL path, by using a double-underscore to represent a path
60
 * separator in your action method name.  For example, using the above routing,
61
 * /some/path/list/1 will route to MyClass::action_list__1(), if it exists,
62
 * leaving URLs that have other values in the last part of their path (for
63
 * example, /some/path/list/2) to still route to MyClass::action_list() as
64
 * before (passing 2 as the first argument).
65
 *
66
 * Also note that hyphens in URLs are converted to underscores (or they wouldn't
67
 * be usable as method names), and multiple underscores are contracred to a
68
 * single underscore (so that the path separators can be represented as a
69
 * double-underscode in method names)
70
 */
71
72
// instantiate flight app
73
require 'flight/autoload.php';
74
$app = new flight\Engine();
75
76
// set fight class path
77
$app->path( __DIR__.'/classes' );
78
79
// set app paths
80
$app->set( 'flight.views.path', 'app/views' );
81
$app->path( 'app/controllers' );
82
83
// config
84
$app->set( 'flight.log_errors', true );
85
86
// add class method router advice to route() method
87
$app->before( 'route',
88
	function( &$params, &$output ) use( &$app )
89
	{
90
		// is the callback an array containing only a classname?
91
		if( count( $params ) >= 2 &&
92
			is_array( $params[ 1 ] ) &&
93
			count( $params[ 1 ] ) == 1 )
94
		{
95
			$class = $params[ 1 ][ 0 ];
96
97
			// fix-up pattern
98
			$params[ 0 ] = preg_replace( '/\/$/', '', $params[ 0 ] ).'/*';
99
100
			// replace callback with method router
101
			$params[ 1 ] =
102
				function( $route ) use( &$app, &$class )
103
				{
104
					// clean up splat
105
					$splat = preg_replace(
106
						array( '/(.)\/$/', '/-/', '/_+/' ),
107
						array( '\1', '_', '_' ), $route->splat );
108
109
					// default action?
110
					if( $splat === '' ) {
111
						if( method_exists( $class, 'default_action' ) ) {
112
							$obj = new $class();
113
							return $obj->default_action();
114
						}
115
					}
116
					else {
117
						// method parts
118
						$parts = explode( '/', $splat );
119
120
						// find method
121
						$params = array();
122
						while( count( $parts ) )
123
						{
124
							// check to see if combined parts make a method name
125
							$method = 'action_'.implode( '__', $parts );
126
							if( method_exists( $class, $method ) ) {
127
								$obj = new $class();
128
								return call_user_func_array(
129
									array( $obj, $method ), $params );
130
							}
131
132
							// discard last part as a param and keep looking
133
							array_unshift( $params, array_pop( $parts ) );
134
						}
135
					}
136
137
					// 404
138
					$app->notFound();
139
				};
140
141
			// pass route to callback
142
			$params[ 2 ] = true;
143
		}
144
	} );
145
146
// add $app to view parameters
147
$app->view()->set( 'app', $app );