/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
/**
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
4
 * Slight: an extension to Flight, in the form of useful configuration changes
1 by Tim Marston
initial commit
5
 * and extra library code.
6
 *
7
 * @copyright Copyright (c) 2015, Tim Marston <tim@ed.am>
8
 * @licence   MIT
9
 *
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
10
 * Slight brings the following changes to Flight:
11
 *
12
 *  1. A global $app object.
13
 *
14
 *  2. App directories.
1 by Tim Marston
initial commit
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
 *
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
58
 * You can also write "specialisations" of actions, where an action has a
59
 * specific argument value.  This is achieved by using a double underscore in
60
 * the method name to represent the path separator.  For example, using the
61
 * above routing, /some/path/list/1 will route to MyClass::action_list__1(), if
62
 * it exists, leaving URLs that have other values in the last part of their path
63
 * (for example, /some/path/list/2) to still route to MyClass::action_list() as
1 by Tim Marston
initial commit
64
 * before (passing 2 as the first argument).
65
 *
66
 * Also note that hyphens in URLs are converted to underscores (or they wouldn't
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
67
 * be usable as method names), and multiple underscores are contracted to a
1 by Tim Marston
initial commit
68
 * single underscore (so that the path separators can be represented as a
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
69
 * double-underscore in method names)
70
 *
71
 *  4. Add database connection and classes
72
 *
73
 * The new method connect() takes an array of connection parameters, as follows:
74
 *     type: the database type (defaults to mysql)
75
 *     host: the database hostname (defaults to localhost)
76
 *     port: database port number (optional)
77
 *     dbname: database name
78
 *     username: authentication user
79
 *     password: authentication password
80
 *     prefix: a table prefix
81
 *
82
 * This makes a PDO connection available at $app->db().
83
 *
84
 * A Model base class is available, for an app to base it's own models on.
85
 *
86
 *  5. Helper functions
87
 *
88
 * The following helper functions are provided:
89
 *
90
 * e()
91
 *     This is a global function which escapes text for use in HTML.
92
 * $app->link( 'some/where' )
93
 *     Constructs and returns links, taking in to account the base_url.
1 by Tim Marston
initial commit
94
 */
95
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
96
97
// instantiate global flight app
1 by Tim Marston
initial commit
98
require 'flight/autoload.php';
99
$app = new flight\Engine();
100
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
101
// set slight class path
1 by Tim Marston
initial commit
102
$app->path( __DIR__.'/classes' );
103
104
// set app paths
105
$app->set( 'flight.views.path', 'app/views' );
106
$app->path( 'app/controllers' );
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
107
$app->path( 'app/models' );
1 by Tim Marston
initial commit
108
109
// config
110
$app->set( 'flight.log_errors', true );
111
112
// add class method router advice to route() method
113
$app->before( 'route',
114
	function( &$params, &$output ) use( &$app )
115
	{
116
		// is the callback an array containing only a classname?
117
		if( count( $params ) >= 2 &&
118
			is_array( $params[ 1 ] ) &&
119
			count( $params[ 1 ] ) == 1 )
120
		{
121
			$class = $params[ 1 ][ 0 ];
122
123
			// fix-up pattern
124
			$params[ 0 ] = preg_replace( '/\/$/', '', $params[ 0 ] ).'/*';
125
126
			// replace callback with method router
127
			$params[ 1 ] =
128
				function( $route ) use( &$app, &$class )
129
				{
130
					// clean up splat
131
					$splat = preg_replace(
132
						array( '/(.)\/$/', '/-/', '/_+/' ),
133
						array( '\1', '_', '_' ), $route->splat );
134
135
					// default action?
136
					if( $splat === '' ) {
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
137
						if( method_exists( $class, 'default_action' ) )
138
						{
139
							// set controller url
140
							$app->set( 'controller', '' );
141
142
							// instantiate class and call method
1 by Tim Marston
initial commit
143
							$obj = new $class();
144
							return $obj->default_action();
145
						}
146
					}
147
					else {
148
						// method parts
149
						$parts = explode( '/', $splat );
150
151
						// find method
152
						$params = array();
153
						while( count( $parts ) )
154
						{
155
							// check to see if combined parts make a method name
156
							$method = 'action_'.implode( '__', $parts );
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
157
							if( method_exists( $class, $method ) )
158
							{
159
								// set controller url
160
								$controller = explode( '/', preg_replace(
161
									array( '/^\//', '/\/$/' ), '',
162
									$app->request()->url ) );
163
								array_splice( $controller, -count( $params ) );
164
								$app->set( 'controller',
165
									join( '/', $controller ) );
166
167
								// instantiate class and call method
1 by Tim Marston
initial commit
168
								$obj = new $class();
169
								return call_user_func_array(
170
									array( $obj, $method ), $params );
171
							}
172
173
							// discard last part as a param and keep looking
174
							array_unshift( $params, array_pop( $parts ) );
175
						}
176
					}
177
178
					// 404
179
					$app->notFound();
180
				};
181
182
			// pass route to callback
183
			$params[ 2 ] = true;
184
		}
185
	} );
186
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
187
// add $app to global view parameters
1 by Tim Marston
initial commit
188
$app->view()->set( 'app', $app );
2 by Tim Marston
renamed project; added connect() method and e() and link() helpers
189
190
// add connect() method
191
$app->map( 'connect',
192
	function( $params ) use( &$app )
193
	{
194
		// default values
195
		$params += array( 'type' => 'mysql', 'host' => 'localhost' );
196
197
		// construct pdo string
198
		$pdostr = $params[ 'type' ].':host='.$params[ 'host' ];
199
		if( isset( $params[ 'port' ] ) ) $pdostr .= ';port='.$params[ 'port' ];
200
		$pdostr .= ';dbname='.$params[ 'dbname' ];
201
202
		// register DB class
203
		$app->register( 'db', 'PDO',
204
			array( $pdostr, $params[ 'username' ], $params[ 'password' ] ) );
205
206
		// set variables
207
		if( isset( $params[ 'prefix' ] ) ) {
208
			$app->set( 'dbprefix', $params[ 'prefix' ] );
209
		}
210
	} );
211
212
// add general html escaping function
213
function e( $text )
214
{
215
	return htmlspecialchars( $text, ENT_COMPAT | ENT_HTML5 );
216
}
217
218
// add link helper
219
$app->map( 'link',
220
	function( $url = null ) use( &$app )
221
	{
222
		if( is_null( $url ) ) $url = $app->get( 'controller' );
223
		$url = preg_replace( array( '/^\//', '/\/$/' ), '', $url );
224
		return $app->get( 'base_url' ).'/'.$url;
225
	} );