Updated to KH 3.3 and improved

This commit is contained in:
Deon George
2013-04-13 16:17:56 +10:00
parent 6f50463ec7
commit 6f7913d363
1551 changed files with 96188 additions and 29813 deletions

View File

@@ -1,6 +1,6 @@
# Loading Classes
Kohana takes advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php). This removes the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class. When you use a class Kohana will find and include the class file for you. For instance, when you want to use the [Cookie::set] method, you simply call:
Kohana supports the [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) autoloading specification as of version 3.3. This allows you to take advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php), removing the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class. When you use a class Kohana will find and include the class file for you. For instance, when you want to use the [Cookie::set] method, you simply call:
Cookie::set('mycookie', 'any string value');
@@ -12,9 +12,9 @@ Classes are loaded via the [Kohana::auto_load] method, which makes a simple conv
1. Classes are placed in the `classes/` directory of the [filesystem](files)
2. Any underscore characters in the class name are converted to slashes
2. The filename is lowercase
2. The filename must match the case of the class
When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/session/cookie.php`.
When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/Session/Cookie.php`.
If your classes do not follow this convention, they cannot be autoloaded by Kohana. You will have to manually included your files, or add your own [autoload function.](http://us3.php.net/manual/en/function.spl-autoload-register.php)
@@ -24,7 +24,10 @@ Kohana's default autoloader is enabled in `application/bootstrap.php` using [spl
spl_autoload_register(array('Kohana', 'auto_load'));
This allows [Kohana::auto_load] to attempt to find and include any class that does not yet exist when the class is first used.
This allows [Kohana::auto_load] to attempt to find and include any class that does not yet exist when the class is first used as long as it follows the PSR-0 specification. If you wish to support the previous Kohana filename convention (using lowercase filesnames), an additional autoloader is provided by Kohana:
spl_autoload_register(array('Kohana', 'auto_load_lowercase'));
### Example: Zend
@@ -57,13 +60,13 @@ Somewhere in `application/bootstrap.php`, copy the following code:
You can now autoload any Zend Framework classes from inside your Kohana application.
if ($validate($_POST))
if ($validate($this->request->post()))
{
$mailer = new Zend_Mail;
$mailer->setBodyHtml($view)
->setFrom(Kohana::config('site')->email_from)
->setFrom(Kohana::$config->load('site')->email_from)
->addTo($email)
->setSubject($message)
->send();
}
}

View File

@@ -6,27 +6,27 @@ The bootstrap is located at `application/bootstrap.php`. It is responsible for
## Environment setup
First the bootstrap sets the timezone and the locale, and adds Kohana's autoloader so the [cascading filesystem](files) works. You could add any other settings that all your application needed here.
The bootstrap first sets the timezone and locale, and then adds Kohana's autoloader so the [cascading filesystem](files) works. You could add any other settings that all your application needed here.
~~~
// Sample excerpt from bootstrap.php with comments trimmed down
// Set the default time zone.
date_default_timezone_set('America/Chicago');
// Set the default locale.
setlocale(LC_ALL, 'en_US.utf-8');
// Enable the Kohana auto-loader.
spl_autoload_register(array('Kohana', 'auto_load'));
// Enable the Kohana auto-loader for unserialization.
ini_set('unserialize_callback_func', 'spl_autoload_call');
~~~
## Initialization and Configuration
Kohana is then initialized by calling [Kohana::init], and the log and [config](files/config) reader/writers are enabled.
Kohana is then initialized by calling [Kohana::init], and the log and [config](files/config) reader/writers are enabled.
~~~
// Sample excerpt from bootstrap.php with comments trimmed down
@@ -48,7 +48,7 @@ You can add conditional statements to make the bootstrap have different values b
~~~
// Excerpt from http://github.com/isaiahdw/kohanaphp.com/blob/f2afe8e28b/application/bootstrap.php
... [trimmed]
/**
* Set the environment status by the domain.
*/
@@ -56,11 +56,11 @@ if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE)
{
// We are live!
Kohana::$environment = Kohana::PRODUCTION;
// Turn off notices and strict errors
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT);
}
/**
* Initialize Kohana, setting the default options.
... [trimmed]
@@ -82,7 +82,7 @@ Kohana::init(array(
**Read the [Modules](modules) page for a more detailed description.**
[Modules](modules) are then loaded using [Kohana::modules()]. Including modules is optional.
[Modules](modules) are then loaded using [Kohana::modules()]. Including modules is optional.
Each key in the array should be the name of the module, and the value is the path to the module, either relative or absolute.
~~~
@@ -99,13 +99,13 @@ Kohana::modules(array(
**Read the [Routing](routing) page for a more detailed description and more examples.**
[Routes](routing) are then defined via [Route::set()].
[Routes](routing) are then defined via [Route::set()].
~~~
// The default route that comes with Kohana 3
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'controller' => 'Welcome',
'action' => 'index',
));
~~~

View File

@@ -0,0 +1,193 @@
# Configuration
By default Kohana is setup to load configuration values from [config files](files/config) in the
cascading filesystem. However, it is very easy to adapt it to load config values in other
locations/formats.
## Sources
The system is designed around the concept of **Config Sources**, which loosely means a method of
storing configuration values.
To read config from a source you need a **Config Reader**. Similarly, to write config to a source
you need a **Config Writer**.
Implementing them is as simple as extending the
[Kohana_Config_Reader] / [Kohana_Config_Writer] interfaces:
class Kohana_Config_Database_Reader implements Kohana_Config_Reader
class Kohana_Config_Database_Writer extends Kohana_Config_Database_Reader implements Kohana_Config_Writer
You'll notice in the above example that the Database Writer extends the Database Reader.
This is the convention with config sources, the reasoning being that if you can write to a
source chances are you can also read from it as well. However, this convention is not enforced
and is left to the developer's discretion.
## Groups
In order to aid organisation config values are split up into logical "groups". For example,
database related settings go in a `database` group, and session related settings go in a
`session` group.
How these groups are stored/organised is up to the config source. For example, the file source
puts different config groups into different files (`database.php`, `session.php`) whereas
the database source uses a column to distinguish between groups.
To load a config group simply call `Kohana::$config->load()` with the name of the group you wish to load:
$config = Kohana::$config->load('my_group');
`load()` will return an instance of [Config_Group] which encapsulates the config values and ensures
that any modifications made will be passed back to the config writers.
To get a config value from a [Config_Group] object simply call [Config_Group::get]:
$config = Kohana::$config->load('my_group');
$value = $config->get('var');
To modify a value call [Config_Group::set]:
$config = Kohana::$config->load('my_group');
$config->set('var', 'new_value');
### Alternative methods for getting / setting config
In addition to the methods described above you can also access config values using dots to outline a path
from the config group to the value you want:
// Config file: database.php
return array(
'default' => array(
'connection' => array(
'hostname' => 'localhost'
)
)
);
// Code which needs hostname:
$hostname = Kohana::$config->load('database.default.connection.hostname');
Which is equivalent to:
$config = Kohana::$config->load('database')->get('default');
$hostname = $config['connection']['hostname'];
Obviously this method is a lot more compact than the original. However, please bear in mind that using
`dot.notation` is a _lot_ slower than calling `get()` and traversing the array yourself. Dot notation
can be useful if you only need one specific variable, but otherwise it's best to use `get()`.
As [Config_Group] extends [Array_Object](http://php.net/manual/en/class.arrayobject.php) you can also use array
syntax to get/set config vars:
$config = Kohana::$config->load('database');
// Getting the var
$hostname = $config['default']['connection']['hostname'];
// Setting the var
$config['default']['connection']['hostname'] = '127.0.0.1';
Again, this syntax is more costly than calling `get()` / `set()`.
## Config Merging
One of the useful features of the config system is config group merging. This works in a similar way
to the cascading filesystem, with configuration from lower sources lower down the source stack being
merged with sources further up the stack.
If two sources contain the same config variables then the one from the source further up the stack will
override the one from the "lower" source. However, if the source from higher up the stack does not contain
a particular config variable but a source lower down the stack does then the value from the lower source will
be used.
The position of sources in the stack is determined by how they are loaded in your bootstrap.
By default when you load a source it is pushed to the top of a stack:
// Stack: <empty>
Kohana::$config->attach(new Config_File);
// Stack: Config_File
Kohana::$config->attach(new Config_Database);
// Stack: Config_Database, Config_File
In the example above, any config values found in the database will override those found in the filesystem.
For example, using the setup outlined above:
// Configuration in the filesystem:
email:
sender:
email: my.awesome.address@example.com
name: Unknown
method: smtp
// Configuration in the database:
email:
sender:
email: my.supercool.address@gmail.com
name: Kohana Bot
// Configuration returned by Kohana::$config->load('email')
email:
sender:
email: my.supercool.address@gmail.com
name: Kohana Bot
method: smtp
[!!] **Note:** The above syntax is simply pseudo code to illustrate the concept of config merging.
On some occasions you may want to append a config source to the bottom of the stack, to do this pass `FALSE`
as the second parameter to `attach()`:
// Stack: <empty>
Kohana::$config->attach(new Config_File);
// Stack: Config_File
Kohana::$config->attach(new Config_Database, FALSE);
// Stack: Config_File, Config_Database
In this example, any values found in the filesystem will override those found in the db. For example:
// Configuration in the filesystem:
email:
sender:
email: my.awesome.address@example.com
name: Unknown
method: smtp
// Configuration in the database:
email:
sender:
email: my.supercool.address@gmail.com
name: Kohana Bot
// Configuration returned by Kohana::$config->load('email')
email:
sender:
email: my.awesome.address@example.com
name: Unknown
method: smtp
## Using different config sources based on the environment
In some situations you'll need to use different config values depending on which state `Kohana::$environment`
is in. Unit testing is a prime example of such a situation. Most setups have two databases; one for normal
development and a separate one for unit testing (to isolate the tests from your development).
In this case you still need access to the config settings stored in the `config` directory as it contains generic
settings that are needed whatever environment your application is in (e.g. encryption settings),
so replacing the default `Config_File` source isn't really an option.
To get around this you can attach a separate config file reader which loads its config from a subdir of `config` called
"testing":
Kohana::$config->attach(new Config_File);
Kohana::$config->attach(new Config_Database);
if (Kohana::$environment === Kohana::TESTING)
{
Kohana::$config->attach(new Config_File('config/testing'));
}
During normal development the config source stack looks like `Config_Database, Config_File('config')`. However,
when `Kohana::$environment === Kohana::TESTING` the stack looks like `Config_File('config/testing'), Config_Database, Config_File('config')`

View File

@@ -8,8 +8,8 @@ Class names in Kohana follow a strict convention to facilitate [autoloading](aut
The following conventions apply:
1. CamelCased class names should not be used, except when it is undesirable to create a new directory level.
2. All class file names and directory names are lowercase.
1. CamelCased class names should be used when it is undesirable to create a new directory level.
2. All class file names and directory names must match the case of the class as per [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md).
3. All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](files).
### Examples {#class-name-examples}
@@ -18,11 +18,12 @@ Remember that in a class, an underscore means a new directory. Consider the foll
Class Name | File Path
----------------------|-------------------------------
Controller_Template | classes/controller/template.php
Model_User | classes/model/user.php
Database | classes/database.php
Database_Query | classes/database/query.php
Form | classes/form.php
Controller_Template | classes/Controller/Template.php
Model_User | classes/Model/User.php
Model_BlogPost | classes/Model/BlogPost.php
Database | classes/Database.php
Database_Query | classes/Database/Query.php
Form | classes/Form.php
## Coding Standards
@@ -388,7 +389,7 @@ Use //, preferably above the line of code you're commenting on. Leave a space af
When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster.
// Correct:
if (preg_match('/abc/i'), $str)
if (preg_match('/abc/i', $str))
// Incorrect:
if (eregi('abc', $str))

View File

@@ -41,36 +41,24 @@ Errors should **always** be displayed, even in production, because it allows you
Kohana comes with a robust system for handing http errors. It includes exception classes for each http status code. To trigger a 404 in your application (the most common scenario):
throw new HTTP_Exception_404('File not found!');
throw HTTP_Exception::factory(404, 'File not found!');
There is no default method to handle these errors in Kohana. It's recommended that you setup an exception handler (and register it) to handle these kinds of errors. Here's a simple example that would go in */application/classes/foobar/exception/handler.php*:
To register error pages for these, using 404 as an example:
class Foobar_Exception_Handler
{
public static function handle(Exception $e)
{
switch (get_class($e))
{
case 'Http_Exception_404':
$response = new Response;
$response->status(404);
$view = new View('error_404');
$view->message = $e->getMessage();
$view->title = 'File Not Found';
echo $response->body($view)->send_headers()->body();
return TRUE;
break;
default:
return Kohana_Exception::handler($e);
break;
}
}
}
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
And put something like this in your bootstrap to register the handler.
public function get_response()
{
$response = Response::factory();
set_exception_handler(array('Foobar_Exception_Handler', 'handle'));
$view = View::factory('errors/404');
> *Note:* Be sure to place `set_exception_handler()` **after** `Kohana::init()` in your bootstrap, or it won't work.
> If you receive *Fatal error: Exception thrown without a stack frame in Unknown on line 0*, it means there was an error within your exception handler. If using the example above, be sure *404.php* exists under */application/views/error/*.
// We're inside an instance of Exception here, all the normal stuff is available.
$view->message = $this->getMessage();
$response->body($view->render());
return $response;
}
}

View File

@@ -1,16 +1,16 @@
# Transparent Class Extension
The [cascading filesystem](files) allows transparent class extension. For instance, the class [Cookie] is defined in `SYSPATH/classes/cookie.php` as:
The [cascading filesystem](files) allows transparent class extension. For instance, the class [Cookie] is defined in `SYSPATH/classes/Cookie.php` as:
class Cookie extends Kohana_Cookie {}
The default Kohana classes, and many extensions, use this definition so that almost all classes can be extended. You extend any class transparently, by defining your own class in `APPPATH/classes/cookie.php` to add your own methods.
The default Kohana classes, and many extensions, use this definition so that almost all classes can be extended. You extend any class transparently, by defining your own class in `APPPATH/classes/Cookie.php` to add your own methods.
[!!] You should **never** modify any of the files that are distributed with Kohana. Always make modifications to classes using transparent extension to prevent upgrade issues.
For instance, if you wanted to create method that sets encrypted cookies using the [Encrypt] class, you would create a file at `application/classes/cookie.php` that extends Kohana_Cookie, and adds your functions:
For instance, if you wanted to create method that sets encrypted cookies using the [Encrypt] class, you would create a file at `APPPATH/classes/Cookie.php` that extends Kohana_Cookie, and adds your functions:
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') OR die('No direct script access.');
class Cookie extends Kohana_Cookie {
@@ -54,13 +54,13 @@ Now calling `Cookie::encrypt('secret', $data)` will create an encrypted cookie w
## How it works
To understand how this works, let's look at what happens normally. When you use the Cookie class, [Kohana::autoload] looks for `classes/cookie.php` in the [cascading filesystem](files). It looks in `application`, then each module, then `system`. The file is found in `system` and is included. Of coures, `system/classes/cookie.php` is just an empty class which extends `Kohana_Cookie`. Again, [Kohana::autoload] is called this time looking for `classes/kohana/cookie.php` which it finds in `system`.
To understand how this works, let's look at what happens normally. When you use the Cookie class, [Kohana::autoload] looks for `classes/Cookie.php` in the [cascading filesystem](files). It looks in `application`, then each module, then `system`. The file is found in `system` and is included. Of course, `system/classes/Cookie.php` is just an empty class which extends `Kohana_Cookie`. Again, [Kohana::autoload] is called this time looking for `classes/Kohana/Cookie.php` which it finds in `system`.
When you add your transparently extended cookie class at `application/classes/cookie.php` this file essentially "replaces" the file at `system/classes/cookie.php` without actually touching it. This happens because this time when we use the Cookie class [Kohana::autoload] looks for `classes/cookie.php` and finds the file in `application` and includes that one, instead of the one in system.
When you add your transparently extended cookie class at `application/classes/Cookie.php` this file essentially "replaces" the file at `system/classes/Cookie.php` without actually touching it. This happens because this time when we use the Cookie class [Kohana::autoload] looks for `classes/Cookie.php` and finds the file in `application` and includes that one, instead of the one in system.
## Example: changing [Cookie] settings
If you are using the [Cookie](cookies) class, and want to change a setting, you should do so using transparent extension, rather than editing the file in the system folder. If you edit it directly, and in the future you upgrade your Kohana version by replacing the system folder, your changes will be reverted and your cookies will probably be invalid. Instead, create a cookie.php file either in `application/classes/cookie.php` or a module (`MODPATH/<modulename>/classes/cookie.php`).
If you are using the [Cookie](cookies) class, and want to change a setting, you should do so using transparent extension, rather than editing the file in the system folder. If you edit it directly, and in the future you upgrade your Kohana version by replacing the system folder, your changes will be reverted and your cookies will probably be invalid. Instead, create a Cookie.php file either in `application/classes/Cookie.php` or a module (`MODPATH/<modulename>/classes/Cookie.php`).
class Cookie extends Kohana_Cookie {
@@ -86,7 +86,7 @@ TODO: Provide some links to modules on github, etc that have examples of transpa
## Multiple Levels of Extension
If you are extending a Kohana class in a module, you should maintain transparent extensions. In other words, do not include any variables or function in the "base" class (eg. Cookie). Instead make your own namespaced class, and have the "base" class extend that one. With our Encrypted cookie example we can create `MODPATH/mymod/encrypted/cookie.php`:
If you are extending a Kohana class in a module, you should maintain transparent extensions. In other words, do not include any variables or function in the "base" class (eg. Cookie). Instead make your own namespaced class, and have the "base" class extend that one. With our Encrypted cookie example we can create `MODPATH/mymod/Encrypted/Cookie.php`:
class Encrypted_Cookie extends Kohana_Cookie {
@@ -94,7 +94,7 @@ If you are extending a Kohana class in a module, you should maintain transparent
}
And create `MODPATH/mymod/cookie.php`:
And create `MODPATH/mymod/Cookie.php`:
class Cookie extends Encrypted_Cookie {}

View File

@@ -21,7 +21,7 @@ This image is only shows certain files, but we can use it to illustrate some exa
* If we used `View::factory('welcome')` it would call `Kohana::find_file('views','welcome')` which would return `application/views/welcome.php` because it takes precidence over `modules/common/views/welcome.php`. By doing this, you can overwrite things in a module without editing the modules files.
* If use the Cookie class, [Kohana::auto_load] will call `Kohana::find_file('classes', 'cookie')` which will return `application/classes/cookie.php`. Assuming Cookie extends Kohana_Cookie, the autoloader would then call `Kohana::find_file('classes','kohana/cookie')` which will return `system/classes/kohana/cookie.php` because that file does not exist anywhere higher in the cascade. This is an example of [transparent extension](extension).
* If use the Cookie class, [Kohana::auto_load] will call `Kohana::find_file('classes', 'Cookie')` which will return `application/classes/Cookie.php`. Assuming Cookie extends Kohana_Cookie, the autoloader would then call `Kohana::find_file('classes','Kohana/Cookie')` which will return `system/classes/Kohana/Cookie.php` because that file does not exist anywhere higher in the cascade. This is an example of [transparent extension](extension).
* If you used `View::factory('user')` it would call `Kohana::find_file('views','user')` which would return `modules/common/views/user.php`.
@@ -32,10 +32,10 @@ This image is only shows certain files, but we can use it to illustrate some exa
The top level directories of the application, module, and system paths have the following default directories:
classes/
: All classes that you want to [autoload](autoloading) should be stored here. This includes [controllers](mvc/controllers), [models](mvc/models), and all other classes. All classes must follow the [class naming conventions](conventions#class-names-and-file-location).
: All classes that you want to [autoload](autoloading) should be stored here. This includes [controllers](mvc/controllers), [models](mvc/models), and all other classes. All classes must follow the [class naming conventions](conventions#class-names-and-file-location) including matching the case of the class i.e. Kohana_Cookie should be stored in classes/Kohana/Cookie.php and not classes/kohana/cookie.php.
config/
: Configuration files return an associative array of options that can be loaded using [Kohana::config]. Config files are merged rather than overwritten by the cascade. See [config files](files/config) for more information.
: Configuration files return an associative array of options that can be loaded using [Kohana::$config]. Config files are merged rather than overwritten by the cascade. See [config files](files/config) for more information.
i18n/
: Translation files return an associative array of strings. Translation is done using the `__()` method. To translate "Hello, world!" into Spanish, you would call `__('Hello, world!')` with [I18n::$lang] set to "es-es". I18n files are merged rather than overwritten by the cascade. See [I18n files](files/i18n) for more information.
@@ -53,8 +53,8 @@ views/
The path to any file within the filesystem can be found by calling [Kohana::find_file]:
// Find the full path to "classes/cookie.php"
$path = Kohana::find_file('classes', 'cookie');
// Find the full path to "classes/Cookie.php"
$path = Kohana::find_file('classes', 'Cookie');
// Find the full path to "views/user/login.php"
$path = Kohana::find_file('views', 'user/login');

View File

@@ -12,7 +12,7 @@ Kohana 3 does not differentiate between "helper" classes and "library" classes l
To create a new class, simply place a file in the `classes/` directory at any point in the [Cascading Filesystem](files), that follows the [Class naming conventions](conventions#class-names-and-file-location). For example, lets create a `Foobar` class.
// classes/foobar.php
// classes/Foobar.php
class Foobar {
static function magic() {
@@ -24,7 +24,7 @@ We can now call `Foobar::magic()` any where and Kohana will [autoload](autoloadi
We can also put classes in subdirectories.
// classes/professor/baxter.php
// classes/Professor/Baxter.php
class Professor_Baxter {
static function teach() {

View File

@@ -2,7 +2,7 @@
Configuration files are used to store any kind of configuration needed for a module, class, or anything else you want. They are plain PHP files, stored in the `config/` directory, which return an associative array:
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'setting' => 'value',
@@ -13,26 +13,8 @@ Configuration files are used to store any kind of configuration needed for a mod
If the above configuration file was called `myconf.php`, you could access it using:
$config = Kohana::config('myconf');
$options = $config['options'];
[Kohana::config] also provides a shortcut for accessing individual keys from configuration arrays using "dot paths" similar to [Arr::path].
Get the "options" array:
$options = Kohana::config('myconf.options');
Get the "foo" key from the "options" array:
$foo = Kohana::config('myconf.options.foo');
Configuration arrays can also be accessed as objects, if you prefer that method:
$options = Kohana::config('myconf')->options;
Please note that you can only access the top level of keys as object properties, all child keys must be accessed using standard array syntax:
$foo = Kohana::config('myconf')->options['foo'];
$config = Kohana::$config->load('myconf');
$options = $config->get('options')
## Merge
@@ -42,7 +24,7 @@ For example, if we wanted to change or add to an entry in the inflector configur
// config/inflector.php
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'irregular' => array(
@@ -57,20 +39,20 @@ Let's say we want a config file to store and easily change things like the title
// config/site.php
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'title' => 'Our Shiny Website',
'analytics' => FALSE, // analytics code goes here, set to FALSE to disable
);
We could now call `Kohana::config('site.title')` to get the site name, and `Kohana::config('site.analytics')` to get the analytics code.
We could now call `Kohana::$config->load('site.title')` to get the site name, and `Kohana::$config->load('site.analytics')` to get the analytics code.
Let's say we want an archive of versions of some software. We could use config files to store each version, and include links to download, documentation, and issue tracking.
// config/versions.php
<?php defined('SYSPATH') or die('No direct script access.');
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'1.0.0' => array(
@@ -93,7 +75,7 @@ Let's say we want an archive of versions of some software. We could use config
You could then do the following:
// In your controller
$view->versions = Kohana::config('versions');
$view->versions = Kohana::$config->load('versions');
// In your view:
foreach ($versions as $version)

View File

@@ -33,4 +33,4 @@ This will look in the `messages/forms/contact.php` for the `[foobar][bar]` key:
## Notes
* Don't use __() in your messages files, as these files can be cached and will not work properly.
* Messages are merged by the cascading file system, not overwritten like config files.
* Messages are merged by the cascading file system, not overwritten like classes and views.

View File

@@ -6,9 +6,9 @@ Every application follows the same flow:
1. The application, module, and system paths are set. (`APPPATH`, `MODPATH`, and `SYSPATH`)
2. Error reporting levels are set.
3. Install file is loaded, if it exists.
4. The [Kohana] class is loaded.
5. The bootstrap file, `APPPATH/bootstrap.php`, is included.
4. The bootstrap file, `APPPATH/bootstrap.php`, is included.
2. Once we are in `bootstrap.php`:
6. The [Kohana] class is loaded.
7. [Kohana::init] is called, which sets up error handling, caching, and logging.
8. [Kohana_Config] readers and [Kohana_Log] writers are attached.
9. [Kohana::modules] is called to enable additional modules.

View File

@@ -1,19 +1,19 @@
# What is Kohana?
Kohana is an open source, [object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming) [MVC](http://wikipedia.org/wiki/ModelViewController "Model View Controller") [web framework](http://wikipedia.org/wiki/Web_Framework) built using [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") by a team of volunteers that aims to be swift, secure, and small.
Kohana is an open source, [object oriented](http://en.wikipedia.org/wiki/Object-oriented_programming) [MVC](http://en.wikipedia.org/wiki/Modelviewcontroller "Model View Controller") [web framework](http://en.wikipedia.org/wiki/Web_application_framework) built using [PHP5](http://php.net/manual/intro-whatis "PHP Hypertext Preprocessor") by a team of volunteers that aims to be swift, secure, and small.
[!!] Kohana is licensed under a [BSD license](http://kohanaframework.org/license), so you can legally use it for any kind of open source, commercial, or personal project.
## What makes Kohana great?
Anything can be extended using the unique [filesystem](about.filesystem) design, little or no [configuration](about.configuration) is necessary, [error handling](debugging.errors) helps locate the source of errors quickly, and [debugging](debugging) and [profiling](debugging.profiling) provide insight into the application.
Anything can be extended using the unique [filesystem](files) design, little or no [configuration](config) is necessary, [error handling](errors) helps locate the source of errors quickly, and [debugging](debugging) and [profiling](profiling) provide insight into the application.
To help secure your applications, tools for [input validation](security.validation), [signed cookies](security.cookies), [form](security.forms) and [HTML](security.html) generators are all included. The [database](security.database) layer provides protection against [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Of course, all official code is carefully written and reviewed for security.
To help secure your applications, tools for [input validation](security/validation), [signed cookies](security/cookies), [form] and [HTML] generators are all included. The [database](security/database) layer provides protection against [SQL injection](http://wikipedia.org/wiki/SQL_injection). Of course, all official code is carefully written and reviewed for security.
## Contribute to the Documentation
We are working very hard to provide complete documentation. To help improve the guide, please [fork the userguide](http://github.com/kohana/userguide), make your changes, and send a pull request. If you are not familiar with git, you can also submit a [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) (requires registration).
We are working very hard to provide complete documentation. To help improve the guide, please [fork the userguide](http://github.com/kohana/userguide), make your changes, and send a pull request. If you are not familiar with Git, you can also submit a [feature request](http://dev.kohanaframework.org/projects/kohana3/issues) (requires registration).
## Unofficial Documentation
If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/wiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaphp.com/) or [stackoverflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel.
If you are having trouble finding an answer here, have a look through the [unofficial wiki](http://kerkness.ca/kowiki/doku.php). Your answer may also be found by searching the [forum](http://forum.kohanaframework.org/) or [Stack Overflow](http://stackoverflow.com/questions/tagged/kohana) followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode [#kohana](irc://irc.freenode.net/kohana) IRC channel.

View File

@@ -5,8 +5,27 @@
3. Upload the contents of this folder to your webserver.
4. Open `application/bootstrap.php` and make the following changes:
- Set the default [timezone](http://php.net/timezones) for your application.
~~~
// Example of changing timezone from Chicago to Sao Paulo, Brazil
date_default_timezone_set('America/Sao_Paulo');
~~~
- Set the `base_url` in the [Kohana::init] call to reflect the location of the kohana folder on your server relative to the document root.
~~~
// Example of kohana's installation at /var/www/mywebsite and
// Apache's DocumentRoot configured to /var/www
Kohana::init(array(
'base_url' => '/mywebsite',
));
~~~
6. Make sure the `application/cache` and `application/logs` directories are writable by the web server.
~~~
sudo chmod 777 -R application/cache
sudo chmod 777 -R application/logs
~~~
7. Test your installation by opening the URL you set as the `base_url` in your favorite browser.
[!!] Depending on your platform, the installation's subdirs may have lost their permissions thanks to zip extraction. Chmod them all to 755 by running `find . -type d -exec chmod 0755 {} \;` from the root of your Kohana installation.
@@ -19,8 +38,8 @@ Once your install page reports that your environment is set up correctly you nee
![Welcome Page](welcome.png "Example of welcome page")
## Installing Kohana 3.1 From GitHub
## Installing Kohana From GitHub
The [source code](http://github.com/kohana/kohana) for Kohana 3.1 is hosted with [GitHub](http://github.com). To install Kohana using the github source code first you need to install git. Visit [http://help.github.com](http://help.github.com) for details on how to install git on your platform.
The [source code](http://github.com/kohana/kohana) for Kohana is hosted with [GitHub](http://github.com). To install Kohana using the github source code first you need to install [git](http://git-scm.com/). Visit [http://help.github.com](http://help.github.com) for details on how to install git on your platform.
[!!] For more information on installing Kohana using git submodules, see the [Working with Git](tutorials/git) tutorial.

View File

@@ -12,13 +12,14 @@
- [Config Files](files/config)
- [Translation Files](files/i18n)
- [Message Files](files/messages)
- [Configuration](config)
- [Request Flow](flow)
- [Bootstrap](bootstrap)
- [Modules](modules)
- [Routing](routing)
- [Error Handling](errors)
- [Tips & Common Mistakes](tips)
- [Upgrading from v3.0](upgrading)
- [Upgrading from v3.2](upgrading)
- Basic Usage
- [Debugging](debugging)
- [Loading Classes](autoloading)
@@ -43,5 +44,6 @@
- [Content Translation](tutorials/translation)
- [Clean URLs](tutorials/clean-urls)
- [Sharing Kohana](tutorials/sharing-kohana)
- [Kohana as a Library](tutorials/library-kohana)
- [Template Driven Site](tutorials/templates)
- [Working with Git](tutorials/git)

View File

@@ -1,3 +1,3 @@
<http://kohanaframework.org/guide/about.mvc>
Discus the MVC pattern, as it pertains to Kohana. Perhaps have an image, etc.
Discuss the MVC pattern, as it pertains to Kohana. Perhaps have an image, etc.

View File

@@ -8,46 +8,46 @@ Controllers are called by the [Request::execute()] function based on the [Route]
In order to function, a controller must do the following:
* Reside in `classes/controller` (or a sub-directory)
* Filename must be lowercase, e.g. `articles.php`
* Reside in `classes/Controller` (or a sub-directory)
* Filename must match the class name exactly, e.g. `Articles.php`
* The class name must map to the filename (with `/` replaced with `_`) and each word is capitalized
* Must have the Controller class as a (grand)parent
Some examples of controller names and file locations:
// classes/controller/foobar.php
// classes/Controller/Foobar.php
class Controller_Foobar extends Controller {
// classes/controller/admin.php
// classes/Controller/Admin.php
class Controller_Admin extends Controller {
Controllers can be in sub-folders:
// classes/controller/baz/bar.php
// classes/Controller/Baz/Bar.php
class Controller_Baz_Bar extends Controller {
// classes/controller/product/category.php
// classes/Controller/Product/Category.php
class Controller_Product_Category extends Controller {
[!!] Note that controllers in sub-folders can not be called by the default route, you will need to define a route that has a [directory](routing#directory) param or sets a default value for directory.
Controllers can extend other controllers.
// classes/controller/users.php
// classes/Controller/Users.php
class Controller_Users extends Controller_Template
// classes/controller/api.php
// classes/Controller/Api.php
class Controller_Api extends Controller_REST
[!!] [Controller_Template] and [Controller_REST] are some example controllers provided in Kohana.
[!!] [Controller_Template] is an example controller provided in Kohana.
You can also have a controller extend another controller to share common things, such as requiring you to be logged in to use all of those controllers.
// classes/controller/admin.php
// classes/Controller/Admin.php
class Controller_Admin extends Controller {
// This controller would have a before() that checks if the user is logged in
// classes/controller/admin/plugins.php
// classes/Controller/Admin/Plugins.php
class Controller_Admin_Plugins extends Controller_Admin {
// Because this controller extends Controller_Admin, it would have the same logged in check
@@ -62,7 +62,6 @@ Property/method | What it does
[$this->request->route()](../api/Request#property:route) | The [Route] that matched the current request url
[$this->request->directory()](../api/Request#property:directory), <br /> [$this->request->controller](../api/Request#property:controller), <br /> [$this->request->action](../api/Request#property:action) | The directory, controller and action that matched for the current route
[$this->request->param()](../api/Request#param) | Any other params defined in your route
[$this->request->redirect()](../api/Request#redirect) | Redirect the request to a different url
## $this->response
[$this->response->body()](../api/Response#property:body) | The content to return for this request
@@ -74,7 +73,7 @@ Property/method | What it does
You create actions for your controller by defining a public function with an `action_` prefix. Any method that is not declared as `public` and prefixed with `action_` can NOT be called via routing.
An action method will decide what should be done based on the current request, it *controls* the application. Did the user want to save a blog post? Did they provide the necessary fields? Do they have permission to do that? The controller will call other classes, including models, to accomplish this. Every action should set `$this->response->body($view)` to the [view file](mvc/views) to be sent to the browser, unless it [redirected](../api/Request#redirect) or otherwise ended the script earlier.
An action method will decide what should be done based on the current request, it *controls* the application. Did the user want to save a blog post? Did they provide the necessary fields? Do they have permission to do that? The controller will call other classes, including models, to accomplish this. Every action should set `$this->response->body($view)` to the [view file](mvc/views) to be sent to the browser, unless it redirected or otherwise ended the script earlier.
A very basic action method that simply loads a [view](mvc/views) file.
@@ -111,7 +110,7 @@ A view action for a product page.
if ( ! $product->loaded())
{
throw new HTTP_Exception_404('Product not found!');
throw HTTP_Exception::factory(404, 'Product not found!');
}
$this->response->body(View::factory('product/view')
@@ -124,12 +123,12 @@ A user login action.
{
$view = View::factory('user/login');
if ($_POST)
if ($this->request->post())
{
// Try to login
if (Auth::instance()->login(arr::get($_POST, 'username'), arr::get($_POST, 'password')))
if (Auth::instance()->login($this->request->post('username'), $this->request->post('password')))
{
Request::current()->redirect('home');
$this->redirect('home', 302);
}
$view->errors = 'Invalid email or password';
@@ -155,7 +154,7 @@ You can check what action has been requested (via `$this->request->action`) and
// If this user doesn't have the admin role, and is not trying to login, redirect to login
if ( ! Auth::instance()->logged_in('admin') AND $this->request->action !== 'login')
{
$this->request->redirect('admin/login');
$this->redirect('admin/login', 302);
}
}
@@ -179,4 +178,4 @@ In general, you should not have to change the `__construct()` function, as anyth
## Extending other controllers
TODO: More description and examples of extending other controllers, multiple extension, etc.
TODO: More description and examples of extending other controllers, multiple extension, etc.

View File

@@ -56,10 +56,95 @@ To execute a request, use the `execute()` method on it. This will give you a [re
$request = Request::factory('welcome');
$response = $request->execute();
### Header callbacks
The request client supports header callbacks - an array of callbacks that will be triggered when a specified header is included in the response from a server. Header callbacks provide a powerful way to deal with scenarios including authentication, rate limiting, redirects and other application-specific use cases:
$request = Request::factory('http://example.com/user', array(
'header_callbacks' => array(
'Content-Encoding' =>
function (Request $request, Response $response, Request_Client $client)
{
// Uncompress the response
$response->body(GZIP::expand($response->body()));
},
'X-Rate-Limited' =>
function (Request $request, Response $response, Request_Client $client)
{
// Log the rate limit event
// And perhaps set a deadlock in cache to prevent further requests
},
'WWW-Authenticate' =>
function (Request $request, Response $response, Request_Client $client)
{
// Execute a request to refresh your OAuth token somehow
// Have the original request resent
return Request::factory($request->uri())
->query($request->query())
->headers('Authorization', 'token'.$token);
}));
Where multiple headers are present in the response, callbacks will be executed in sequence. Callbacks can be any valid PHP callback type and have three possible return types:
Type | Function
------------------|---------
[Request] object | If a new request is returned, the request client will automatically assign properties, callbacks etc to match the original request and then execute the request. No further callbacks will be triggered for the original request, but the new request may trigger callbacks when executed.
[Response] object | If the callback returns a new response instance it will be returned to the application. No further callbacks will be triggered for the original request. The callback is responsible for setting any relevant callbacks and properties for the request it executes
NULL | The callback can, if required, modify the provided Response object and return NULL. The modified response object will be passed into subsequent callbacks.
#### Nested requests
If your callback returns a new Request object, the request client will apply the same callback and property definitions to it before execution. This allows for nested requests - for example, you might need to re-authenticate before submitting a POST request and then being redirected to a new location. To avoid infinite recursion and fatal errors, the request client keeps track of the number of subrequests and will throw a [Request_Client_Recursion_Exception] if the recursion gets too deep. This behaviour is controlled by two properties: [Request_Client::callback_depth()] and [Request_Client::max_callback_depth()]. The default limit is 5 subrequests.
If your callback executes a new request itself and returns the response, it is responsible for dealing with any callbacks and request nesting itself. You may find the [Request_Client::assign_client_properties()] method useful in this case.
#### Callback parameters
Arbitrary parameters can be passed to the callbacks through the [Request_Client::callback_params()] property:
$request = Request::factory('http://example.com/foo', array(
'header_callbacks' => array(
'X-Custom-1' =>
function (Request $request, Response $response, Request_Client $client)
{
// Do something that needs an external parameter
if ($client->callback_params('foo') == 'bar')
{
// etc
}
},
)
'callback_params' => array(
'foo' => 'bar'
)
));
// later on
$request->client()->callback_params('foo',FALSE);
As with nested requests, callback_params will automatically be passed to subrequests if the callback returns a new Request object. If the callback returns a Response object, it is responsible for passing on any relevant parameters.
#### Following redirects
The request client ships with a standard callback to automatically follow redirects - [Request_Client::on_header_location()]. This will recursively follow redirects that are specified with a Location header and a status code in 201, 301, 302, 303, 307. This behaviour is disabled by default, but can be enabled by passing a set of options to the Request's constructor:
$request = Request::factory('http://example.com/redirectme', array(
'follow' => TRUE));
[!!] If you define additional header callbacks of your own, you will need to include the 'Location' callback in your callbacks array.
A number of options are available to control the behaviour of the [Request_Client] when following redirects.
Option |Default |Function
-----------------|------------------------|---------
follow | FALSE |Whether to follow redirects
follow_headers | array('Authorization') |The keys of headers that will be re-sent with the redirected request
strict_redirect | TRUE |Whether to use the original request method following to a 302 redirect (see below)
[!!] HTTP/1.1 specifies that a 302 redirect should be followed using the original request method. However, the vast majority of clients and servers get this wrong, with 302 widely used for 'POST - 302 redirect - GET' patterns. By default, Kohana's client is fully compliant with the HTTP spec. If you need to interact with non-compliant third party sites you may need to set strict_redirect FALSE to force the client to switch to GET following a 302 response.
You can easily alter this behaviour by configuring your own 'Location' header callback.
## Request Cache Control
You can cache requests for fast execution by passing a cache instance in as the second parameter of factory:
$request = Request::factory('welcome', Cache::instance());
$request = Request::factory('welcome', array('cache'=>Cache::instance()));
TODO

View File

@@ -12,7 +12,7 @@ If you look in `APPPATH/bootstrap.php` you will see the "default" route as follo
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'controller' => 'Welcome',
'action' => 'index',
));
@@ -34,7 +34,7 @@ Lets look at the default route again, the uri is `(<controller>(/<action>(/<id>)
You can use any name you want for your keys, but the following keys have special meaning to the [Request] object, and will influence which controller and action are called:
* **Directory** - The sub-directory of `classes/controller` to look for the controller (\[covered below]\(#directory))
* **Directory** - The sub-directory of `classes/Controller` to look for the controller (\[covered below]\(#directory))
* **Controller** - The controller that the request should execute.
* **Action** - The action method to call.
@@ -42,14 +42,14 @@ You can use any name you want for your keys, but the following keys have special
The Kohana route system uses [perl compatible regular expressions](http://perldoc.perl.org/perlre.html) in its matching process. By default each key (surrounded by `<>`) will match `[^/.,;?\n]++` (or in english: anything that is not a slash, period, comma, semicolon, question mark, or newline). You can define your own patterns for each key by passing an associative array of keys and patterns as an additional third argument to Route::set.
In this example, we have controllers in two directories, `admin` and `affiliate`. Because this route will only match urls that begin with `admin` or `affiliate`, the default route would still work for controllers in `classes/controller`.
In this example, we have controllers in two directories, `admin` and `affiliate`. Because this route will only match urls that begin with `admin` or `affiliate`, the default route would still work for controllers in `classes/Controller`.
Route::set('sections', '<directory>(/<controller>(/<action>(/<id>)))',
array(
'directory' => '(admin|affiliate)'
))
->defaults(array(
'controller' => 'home',
'controller' => 'Home',
'action' => 'index',
));
@@ -57,7 +57,7 @@ You can also use a less restrictive regex to match unlimited parameters, or to i
Route::set('default', '(<controller>(/<action>(/<stuff>)))', array('stuff' => '.*'))
->defaults(array(
'controller' => 'welcome',
'controller' => 'Welcome',
'action' => 'index',
));
@@ -68,6 +68,8 @@ If a key in a route is optional (or not present in the route), you can provide a
[!!] The `controller` and `action` key must always have a value, so they either need to be required in your route (not inside of parentheses) or have a default value provided.
[!!] Kohana automatically converts controllers to follow the standard naming convention. For example /blog/view/123 would look for the controller Controller_Blog in classes/Controller/Blog.php and trigger the action_view() method on it.
In the default route, all the keys are optional, and the controller and action are given a default. If we called an empty url, the defaults would fill in and `Controller_Welcome::action_index()` would be called. If we called `foobar` then only the default for action would be used, so it would call `Controller_Foobar::action_index()` and finally, if we called `foobar/baz` then neither default would be used and `Controller_Foobar::action_baz()` would be called.
TODO: need an example here
@@ -78,42 +80,36 @@ TODO: example of either using directory or controller where it isn't in the rout
### Directory
## Lambda/Callback route logic
## Route Filters
In 3.1, you can specify advanced routing schemes by using lambda routes. Instead of a URI, you can use an anonymous function or callback syntax to specify a function that will process your routes. Here's a simple example:
In 3.3, you can specify advanced routing schemes by using filter callbacks. When you need to match a route based on more than just the URI of a request, for example, based on the method request (GET/POST/DELETE), a filter will allow you to do so. These filters will receive the `Route` object being tested, the currently matched `$params` array, and the `Request` object as the three parameters. Here's a simple example:
If you want to use reverse routing with lambda routes, you must pass the third parameter:
Route::set('testing', function($uri)
Route::set('save-form', 'save')
->filter(function($route, $params, $request)
{
if ($uri == 'foo/bar')
return array(
'controller' => 'welcome',
'action' => 'foobar',
);
},
'foo/bar'
);
As you can see in the below route, the reverse uri parameter might not make sense.
Route::set('testing', function($uri)
{
if ($uri == '</language regex/>(.+)')
if ($request->method() !== HTTP_Request::POST)
{
Cookie::set('language', $match[1]);
return array(
'controller' => 'welcome',
'action' => 'foobar'
);
return FALSE; // This route only matches POST requests
}
},
'<language>/<rest_of_uri>
);
});
If you are using php 5.2, you can still use callbacks for this behavior (this example omits the reverse route):
Filters can also replace or alter the array of parameters:
Route::set('testing', array('Class', 'method_to_process_my_uri'));
Route::set('rest-api', 'api/<action>')
->filter(function($route, $params, $request)
{
// Prefix the method to the action name
$params['action'] = strtolower($request->method()).'_'.$params['action'];
return $params; // Returning an array will replace the parameters
})
->defaults(array(
'controller' => 'api',
));
If you are using php 5.2, you can still use any valid callback for this behavior:
Route::set('testing', 'foo')
->filter(array('Class', 'method_to_process_my_uri'));
## Examples
@@ -127,7 +123,7 @@ There are countless other possibilities for routes. Here are some more examples:
'action' => '(login|logout)'
))
->defaults(array(
'controller' => 'auth'
'controller' => 'Auth'
));
/*
@@ -141,7 +137,7 @@ There are countless other possibilities for routes. Here are some more examples:
'format' => '(rss|atom|json)',
))
->defaults(array(
'controller' => 'feeds',
'controller' => 'Feeds',
'action' => 'status',
));
@@ -153,7 +149,7 @@ There are countless other possibilities for routes. Here are some more examples:
'path' => '[a-zA-Z0-9_/]+',
))
->defaults(array(
'controller' => 'static',
'controller' => 'Static',
'action' => 'index',
));
@@ -176,7 +172,7 @@ There are countless other possibilities for routes. Here are some more examples:
*/
Route::set('search', ':<query>', array('query' => '.*'))
->defaults(array(
'controller' => 'search',
'controller' => 'Search',
'action' => 'index',
));
@@ -212,17 +208,7 @@ For example, with the following route:
'action' => 'index',
));
If a url matches the route, then `Controller_Ads::index()` will be called. You could access the parameters in two ways:
First, any non-special parameters (parameters other than controller, action, and directory) in a route are passed as parameters to the action method in the order they appear in the route. Be sure to define a default value for optional parameters if you don't define them in the route's `->defaults()`.
class Controller_Ads extends Controller {
public function action_index($ad, $affiliate = NULL)
{
}
Secondly, you can access the parameters using the `param()` method of the [Request] class. Again, remember to define a default value (via the second, optional parameter of [Request::param]) if you didn't in `->defaults()`.
If a url matches the route, then `Controller_Ads::index()` will be called. You can access the parameters by using the `param()` method of the controller's [Request]. Remember to define a default value (via the second, optional parameter of [Request::param]) if you didn't in `->defaults()`.
class Controller_Ads extends Controller {
public function action_index()

View File

@@ -29,33 +29,3 @@ There are a few things you'll want to do with your application before moving int
'profile' => Kohana::$environment !== Kohana::PRODUCTION,
'caching' => Kohana::$environment === Kohana::PRODUCTION,
));
`index.php`:
/**
* Execute the main request using PATH_INFO. If no URI source is specified,
* the URI will be automatically detected.
*/
$request = Request::instance($_SERVER['PATH_INFO']);
// Attempt to execute the response
$request->execute();
if ($request->send_headers()->response)
{
// Get the total memory and execution time
$total = array(
'{memory_usage}' => number_format((memory_get_peak_usage() - KOHANA_START_MEMORY) / 1024, 2).'KB',
'{execution_time}' => number_format(microtime(TRUE) - KOHANA_START_TIME, 5).' seconds');
// Insert the totals into the response
$request->response = str_replace(array_keys($total), $total, $request->response);
}
/**
* Display the request response.
*/
echo $request->response;

View File

@@ -1 +1,107 @@
Discuss using encryption, including setting the encryption key in config.
# Encryption
Kohana supports built-in encryption and decryption via the [Encrypt] class, which is a convenient wrapper for the [Mcrypt library](http://www.php.net/mcrypt).
To use the class, first start by ensuring you have the Mcrypt extension loaded to your PHP config. See the [Mcrypt Installation page](http://www.php.net/manual/en/mcrypt.installation.php) on php.net. The Mcrypt extension requires [libmcrypt](http://sourceforge.net/projects/mcrypt/files/).
Next, copy the default config/encryption.php from system/config folder to your application/config folder.
The default Encryption config file that ships with Kohana 3.2.x looks like this:
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
'default' => array(
/**
* The following options must be set:
*
* string key secret passphrase
* integer mode encryption mode, one of MCRYPT_MODE_*
* integer cipher encryption cipher, one of the Mcrpyt cipher constants
*/
'cipher' => MCRYPT_RIJNDAEL_128,
'mode' => MCRYPT_MODE_NOFB,
),
);
A couple of notes about the config.
First, you may have multiple first-level keys other than 'default' if you need to.
In this respect, the config file is similar to having multiple databases defined in your config/database.php file.
Second, notice there is no key provided. You need to add that.
It is strongly recommended that you choose a high-strength random key using the [pwgen linux program](http://linux.die.net/man/1/pwgen)...
shell> pwgen 63 1
trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj
...or by going to [GRC.com/passwords.htm](https://www.grc.com/passwords.htm).
## Complete Config Example
Here's a sample encryption configuration with three types of encryption defined. **If you copy this example, please change your keys!**
<?php defined('SYSPATH') OR die('No direct script access.');
// application/config/encrypt.php
return array(
'default' => array(
'key' => 'trwQwVXX96TIJoKxyBHB9AJkwAOHixuV1ENZmIWyanI0j1zNgSVvqywy044Agaj',
'cipher' => MCRYPT_RIJNDAEL_128,
'mode' => MCRYPT_MODE_NOFB,
),
'blowfish' => array(
'key' => '7bZJJkmNrelj5NaKoY6h6rMSRSmeUlJuTeOd5HHka5XknyMX4uGSfeVolTz4IYy',
'cipher' => MCRYPT_BLOWFISH,
'mode' => MCRYPT_MODE_ECB,
),
'tripledes' => array(
'key' => 'a9hcSLRvA3LkFc7EJgxXIKQuz1ec91J7P6WNq1IaxMZp4CTj5m31gZLARLxI1jD',
'cipher' => MCRYPT_3DES,
'mode' => MCRYPT_MODE_CBC,
),
);
You can view the available encryption ciphers and modes on your system by running...
shell> php -r "print_r(get_defined_constants());" | grep MCRYPT
For more information on Mcrypt ciphers, visit [php.net/mcrypt.ciphers](http://us3.php.net/manual/en/mcrypt.ciphers.php).
## Basic Usage
### Create an instance
To use the Encryption class, obtain an instance of the Encrypt class by calling it's *instance* method,
optionally passing the desired configuration group. If you do not pass a config group to the instance method,
the default group will be used.
$encrypt = Encrypt::instance('tripledes');
### Encoding Data
Next, encode some data using the *encode* method:
$encrypt = Encrypt::instance('tripledes');
$encrypted_data = $encrypt->encode('Data to Encode');
// $encrypted_data now contains pCD5Z6oVdb9hbLxxV+FgGrhwVzZuhQoH
[!!] Raw encrypted strings usually won't print in a browser, and may not store properly in a VARCHAR or TEXT field. For this reason, Kohana's Encrypt class automatically calls base64_encode on encode, and base64_decode on decode, to prevent this problem.
[!!] One word of caution. The length of the encoded data expands quite a bit, so be sure your database column is long enough to store the encrypted data. If even one character is truncated, the data will not be recoverable.
### Decoding Data
To decode some data, load it from the place you stored it (most likely your database) then pass it to the *decode* method:
$encrypt = Encrypt::instance('tripledes');
$decoded_string = $encrypt->decode($encrypted_data);
echo $decoded_string;
// prints 'Data to Encode'
You can't know in advance what the encoded string will be, and it's not reproducible, either.
That is, you can encode the same value over and over, but you'll always obtain a different encoded version,
even without changing your key, cipher and mode. This is because Kohana adds some random entropy before encoding it with your value.
This ensures an attacker cannot easily discover your key and cipher, even given a collection of encoded values.

View File

@@ -2,13 +2,13 @@
*This page needs to be reviewed for accuracy by the development team. Better examples would be helpful.*
Validation can be performed on any array using the [Validation] class. Labels and rules can be attached to a Validate object by the array key, called a "field name".
Validation can be performed on any array using the [Validation] class. Labels and rules can be attached to a Validation object by the array key, called a "field name".
labels
: A label is a human-readable version of the field name.
rules
: A rule is a callback used to decide whether or not to add an error to a field
: A rule is a callback or closure used to decide whether or not to add an error to a field
[!!] Note that any valid [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) can be used as a rule.
@@ -16,9 +16,9 @@ Using `TRUE` as the field name when adding a rule will be applied to all named f
Creating a validation object is done using the [Validation::factory] method:
$post = Validation::factory($_POST);
$object = Validation::factory($array);
[!!] The `$post` object will be used for the rest of this tutorial. This tutorial will show you how to validate the registration of a new user.
[!!] The `$object` object will be used for the rest of this tutorial. This tutorial will show you how to validate the registration of a new user.
## Provided Rules
@@ -32,7 +32,7 @@ Rule name | Function
[Valid::max_length] | Maximum number of characters for value
[Valid::exact_length] | Value must be an exact number of characters
[Valid::email] | An email address is required
[Validate::email_domain] | Check that the domain of the email exists
[Valid::email_domain] | Check that the domain of the email exists
[Valid::url] | Value must be a URL
[Valid::ip] | Value must be an IP address
[Valid::phone] | Value must be a phone number
@@ -50,7 +50,7 @@ Rule name | Function
## Adding Rules
All validation rules are defined as a field name, a method or function (using the [PHP callback](http://php.net/callback) syntax), and an array of parameters:
All validation rules are defined as a field name, a method, a function (using the [PHP callback](http://php.net/callback) syntax) or [closure](http://php.net/manual/functions.anonymous.php), and an array of parameters:
$object->rule($field, $callback, array($parameter1, $parameter2));
@@ -65,6 +65,36 @@ Rules defined in the [Valid] class can be added by using the method name alone.
$object->rule('number', array('Valid', 'phone'));
$object->rule('number', 'Valid::phone');
### Adding Rules for multiple fields together
To validate multiple fields together, you can do something like this:
$object->rule('one', 'only_one', array(':validation', array('one', 'two')));
$object->rule('two', 'only_one', array(':validation', array('one', 'two')));
public function only_one($validation, $fields)
{
// If more than 1 field is set, bail.
$matched = 0;
foreach ($fields as $field)
{
if (isset($validation[$field]))
{
$matched++;
}
}
if ($matched > 0)
{
// Add the error to all concerned fields
foreach ($fields as $field)
{
$validation->error($field, 'only_one');
}
}
}
## Binding Variables
The [Validation] class allows you to bind variables to certain strings so that they can be used when defining rules. Variables are bound by calling the [Validation::bind] method.
@@ -88,7 +118,7 @@ The [Validation] class will add an error for a field if any of the rules associa
Rules added to empty fields will run, but returning `FALSE` will not automatically add an error for the field. In order for a rule to affect empty fields, you must add the error manually by calling the [Validation::error] method. In order to do this, you must pass the validation object to the rule.
$object->rule($field, 'the_rule', array(':validation', ':field'));
public function the_rule($validation, $field)
{
if (something went wrong)
@@ -101,13 +131,15 @@ Rules added to empty fields will run, but returning `FALSE` will not automatical
## Example
To start our example, we will perform validation on a `$_POST` array that contains user registration information:
To start our example, we will perform validation on the HTTP POST data of the current request that contains user registration information:
$post = Validation::factory($_POST);
[!!] In Kohana controllers, we access `$this->request->post()` instead of `$_POST` for better request isolation.
$object = Validation::factory($this->request->post());
Next we need to process the POST'ed information using [Validation]. To start, we need to add some rules:
$post
$object
->rule('username', 'not_empty')
->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD'))
->rule('password', 'not_empty')
@@ -117,13 +149,13 @@ Next we need to process the POST'ed information using [Validation]. To start, we
Any existing PHP function can also be used a rule. For instance, if we want to check if the user entered a proper value for the SSL question:
$post->rule('use_ssl', 'in_array', array(':value', array('yes', 'no')));
$object->rule('use_ssl', 'in_array', array(':value', array('yes', 'no')));
Note that all array parameters must still be wrapped in an array! Without the wrapping array, `in_array` would be called as `in_array($value, 'yes', 'no')`, which would result in a PHP error.
Any custom rules can be added using a [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback]:
$post->rule('username', 'User_Model::unique_username');
$object->rule('username', 'User_Model::unique_username');
The method `User_Model::unique_username()` would be defined similar to:
@@ -172,7 +204,7 @@ First, we need a [View] that contains the HTML form, which will be placed in `ap
[!!] This example uses the [Form] helper extensively. Using [Form] instead of writing HTML ensures that all of the form inputs will properly handle input that includes HTML characters. If you prefer to write the HTML yourself, be sure to use [HTML::chars] to escape user input.
Next, we need a controller and action to process the registration, which will be placed in `application/classes/controller/user.php`:
Next, we need a controller and action to process the registration, which will be placed in `application/classes/Controller/User.php`:
class Controller_User extends Controller {
@@ -180,7 +212,7 @@ Next, we need a controller and action to process the registration, which will be
{
$user = Model::factory('user');
$post = Validate::factory($_POST)
$validation = Validation::factory($this->request->post())
->rule('username', 'not_empty')
->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD'))
->rule('username', array($user, 'unique_username'))
@@ -192,27 +224,27 @@ Next, we need a controller and action to process the registration, which will be
->rule('use_ssl', 'not_empty')
->rule('use_ssl', 'in_array', array(':value', array('yes', 'no')));
if ($post->check())
if ($validation->check())
{
// Data has been validated, register the user
$user->register($post);
$user->register($this->request->post());
// Always redirect after a successful POST to prevent refresh warnings
$this->request->redirect('user/profile');
$this->redirect('user/profile', 302);
}
// Validation failed, collect the errors
$errors = $post->errors('user');
$errors = $validation->errors('user');
// Display the registration form
$this->response->body(View::factory('user/register'))
->bind('post', $post)
->bind('post', $this->request->post())
->bind('errors', $errors);
}
}
We will also need a user model, which will be placed in `application/classes/model/user.php`:
We will also need a user model, which will be placed in `application/classes/Model/User.php`:
class Model_User extends Model {

View File

@@ -10,6 +10,14 @@ You should (almost) never edit the system folder. Any change you want to make t
Kohana 3 [routes](routing) are very powerful and flexible, don't be afraid to use as many as you need to make your app function the way you want!
## Files not found on some systems
As of Kohana 3.3, classes are autoloaded using the case-sensitive PSR-0 autoloader. This means that using the class Foo {} with a file in classes/foo.php will work on case-insensitive file systems (such as the default HFS+ FS used in Mac OS X) but will fail when used on a case-sensitive FS (typical on many production Linux servers).
## Handling lots of routes
Sometimes your application is sufficiently complex that you have many routes and it becomes unmanageable to put them all in bootstrap.php. If this is the case, simply make a `routes.php` file in APPPATH and require that in your bootstrap: `require_once APPPATH.'routes'.EXT;`
## Reflection_Exception
If you get a Reflection_Exception when setting up your site, it is almost certainly because your [Kohana::init] 'base_url' setting is wrong. If your base url is correct something is probably wrong with your [routes](routing).

View File

@@ -32,6 +32,19 @@ Rename `example.htaccess` to only `.htaccess` and alter the `RewriteBase` line t
The rest of the `.htaccess file` rewrites all requests through index.php, unless the file exists on the server (so your css, images, favicon, etc. are still loaded like normal). In most cases, you are done!
### 404 errors
If you get a "404 Not Found" error when trying to view a page then it's likely Apache is not configured to read the `.htaccess` file.
In the main apache configuration file (usually `httpd.conf`), or in the virtual server configuration file, check that the `AccessFileName` directive is set to `.htaccess` and the `AllowOverride` directive is set to `All`.
AccessFileName .htaccess
<Directory "/var/www/html/myapp">
AllowOverride All
</Directory>
### Failed!
If you get a "Internal Server Error" or "No input file specified" error, try changing:
@@ -52,7 +65,7 @@ To something more simple:
### Still Failed!
If you are still getting errors, check to make sure that your host supports URL `mod_rewrite`. If you can change the Apache configuration, add these lines to the the configuration, usually `httpd.conf`:
If you are still getting errors, check to make sure that your host supports URL `mod_rewrite`. If you can change the Apache configuration, add these lines to the configuration, usually `httpd.conf`:
<Directory "/var/www/html/myapp">
Order allow,deny

View File

@@ -1,154 +1,99 @@
# Friendly Error Pages
# Custom Error Pages
By default Kohana 3 doesn't have a method to display friendly error pages like that
seen in Kohana 2; In this short guide you will learn how it is done.
Custom error pages allow you to display a friendly error message to users, rather than the standard Kohana stack trace.
## Prerequisites
You will need `'errors' => TRUE` passed to `Kohana::init`. This will convert PHP
errors into exceptions which are easier to handle.
1. You will need `'errors' => TRUE` passed to [Kohana::init]. This will convert PHP-errors into exceptions which are easier to handle (The default value is `TRUE`).
2. Custom error pages will only be used to handle throw [HTTP_Exception]'s. If you simply set a status of, for example, 404 via [Respose::status] the custom page will not be used.
## 1. An Improved Exception Handler
## Extending the HTTP_Exception classes
Our custom exception handler is self explanatory.
Handling [HTTP_Exception]'s in Kohana has become easier with the changes introduced in 3.3.
class Kohana_Exception extends Kohana_Kohana_Exception {
For each [HTTP_Exception] class we can individually override the generation of the [Response] instance.
public static function handler(Exception $e)
[!!] Note: We can also use HMVC to issue a sub-request to another page rather than generating the [Response] in the [HTTP_Exception] itself.
For example, to handle 404 pages we can do this in APPPATH/classes/HTTP/Exception/404.php:
class HTTP_Exception_404 extends Kohana_HTTP_Exception_404 {
/**
* Generate a Response for the 404 Exception.
*
* The user should be shown a nice 404 page.
*
* @return Response
*/
public function get_response()
{
if (Kohana::DEVELOPMENT === Kohana::$environment)
$view = View::factory('errors/404');
// Remembering that `$this` is an instance of HTTP_Exception_404
$view->message = $this->getMessage();
$response = Response::factory()
->status(404)
->body($view->render());
return $response;
}
}
Another example, this time to handle 401 Unauthorized errors (aka "Not Logged In") we can do this in APPPATH/classes/HTTP/Exception/401.php:
class HTTP_Exception_401 extends Kohana_HTTP_Exception_401 {
/**
* Generate a Response for the 401 Exception.
*
* The user should be redirect to a login page.
*
* @return Response
*/
public function get_response()
{
$response = Response::factory()
->status(401)
->headers('Location', URL::site('account/login'));
return $response;
}
}
Finally, to override the default [Response] for all [HTTP_Exception]'s without a more specific override we can do this in APPPATH/classes/HTTP/Exception.php:
class HTTP_Exception extends Kohana_HTTP_Exception {
/**
* Generate a Response for all Exceptions without a more specific override
*
* The user should see a nice error page, however, if we are in development
* mode we should show the normal Kohana error page.
*
* @return Response
*/
public function get_response()
{
// Lets log the Exception, Just in case it's important!
Kohana_Exception::log($this);
if (Kohana::$environment >= Kohana::DEVELOPMENT)
{
parent::handler($e);
// Show the normal Kohana error page.
return parent::get_response();
}
else
{
try
{
Kohana::$log->add(Log::ERROR, parent::text($e));
// Generate a nicer looking "Oops" page.
$view = View::factory('errors/default');
$attributes = array
(
'action' => 500,
'message' => rawurlencode($e->getMessage())
);
$response = Response::factory()
->status($this->getCode())
->body($view->render());
if ($e instanceof HTTP_Exception)
{
$attributes['action'] = $e->getCode();
}
// Error sub-request.
echo Request::factory(Route::get('error')->uri($attributes))
->execute()
->send_headers()
->body();
}
catch (Exception $e)
{
// Clean the output buffer if one exists
ob_get_level() and ob_clean();
// Display the exception text
echo parent::text($e);
// Exit with an error status
exit(1);
}
return $response;
}
}
}
If we are in the development environment then pass it off to Kohana otherwise:
* Log the error
* Set the route action and message attributes.
* If a `HTTP_Exception` was thrown, then override the action with the error code.
* Fire off an internal sub-request.
The action will be used as the HTTP response code. By default this is: 500 (internal
server error) unless a `HTTP_Response_Exception` was thrown.
So this:
throw new HTTP_Exception_404(':file does not exist', array(':file' => 'Gaia'));
would display a nice 404 error page, where:
throw new Kohana_Exception('Directory :dir must be writable',
array(':dir' => Debug::path(Kohana::$cache_dir)));
would display an error 500 page.
**The Route**
Route::set('error', 'error/<action>(/<message>)', array('action' => '[0-9]++', 'message' => '.+'))
->defaults(array(
'controller' => 'error_handler'
));
## 2. The Error Page Controller
public function before()
{
parent::before();
$this->template->page = URL::site(rawurldecode(Request::$initial->uri()));
// Internal request only!
if (Request::$initial !== Request::$current)
{
if ($message = rawurldecode($this->request->param('message')))
{
$this->template->message = $message;
}
}
else
{
$this->request->action(404);
}
$this->response->status((int) $this->request->action());
}
1. Set a template variable "page" so the user can see what they requested. This
is for display purposes only.
2. If an internal request, then set a template variable "message" to be shown to
the user.
3. Otherwise use the 404 action. Users could otherwise craft their own error messages, eg:
`error/404/email%20your%20login%20information%20to%20hacker%40google.com`
public function action_404()
{
$this->template->title = '404 Not Found';
// Here we check to see if a 404 came from our website. This allows the
// webmaster to find broken links and update them in a shorter amount of time.
if (isset ($_SERVER['HTTP_REFERER']) AND strstr($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== FALSE)
{
// Set a local flag so we can display different messages in our template.
$this->template->local = TRUE;
}
// HTTP Status code.
$this->response->status(404);
}
public function action_503()
{
$this->template->title = 'Maintenance Mode';
}
public function action_500()
{
$this->template->title = 'Internal Server Error';
}
You will notice that each example method is named after the HTTP response code
and sets the request response code.
## 3. Conclusion
So that's it. Now displaying a nice error page is as easy as:
throw new HTTP_Exception_503('The website is down');
}

View File

@@ -28,7 +28,7 @@ Now that the submodules are added, you can commit them:
Next, create the application directory structure. This is the bare minimum required:
mkdir -p application/classes/{controller,model}
mkdir -p application/classes/{Controller,Model}
mkdir -p application/{config,views}
mkdir -m 0777 -p application/{cache,logs}
@@ -38,8 +38,8 @@ If you run `find application` you should see this:
application/cache
application/config
application/classes
application/classes/controller
application/classes/model
application/classes/Controller
application/classes/Model
application/logs
application/views
@@ -51,8 +51,8 @@ We don't want git to track log or cache files, so add a `.gitignore` file to eac
Now we need the `index.php` and `bootstrap.php` files:
wget https://github.com/kohana/kohana/raw/3.1/master/index.php --no-check-certificate
wget https://github.com/kohana/kohana/raw/3.1/master/application/bootstrap.php --no-check-certificate -O application/bootstrap.php
wget https://github.com/kohana/kohana/raw/3.3/master/index.php --no-check-certificate
wget https://github.com/kohana/kohana/raw/3.3/master/application/bootstrap.php --no-check-certificate -O application/bootstrap.php
Commit these changes too:
@@ -77,13 +77,13 @@ To add a new submodule complete the following steps:
At some point you will probably also want to upgrade your submodules. To update all of your submodules to the latest `HEAD` version:
git submodule foreach 'git checkout 3.1/master && git pull origin 3.1/master'
git submodule foreach 'git checkout 3.3/master && git pull origin 3.3/master'
To update a single submodule, for example, `system`:
cd system
git checkout 3.1/master
git pull origin 3.1/master
git checkout 3.3/master
git pull origin 3.3/master
cd ..
git add system
git commit -m 'Updated system to latest version'
@@ -91,7 +91,7 @@ To update a single submodule, for example, `system`:
If you want to update a single submodule to a specific commit:
cd modules/database
git pull origin 3.1/master
git pull origin 3.3/master
git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
cd ../..
git add database
@@ -99,7 +99,7 @@ If you want to update a single submodule to a specific commit:
Note that you can also check out the commit at a tagged official release point, for example:
git checkout 3.1.0
git checkout v3.3.0
Simply run `git tag` without arguments to get a list of all tags.

View File

@@ -8,7 +8,7 @@ We'll start out by creating a very very basic hello world, and then we'll expand
First off we have to make a controller that Kohana can use to handle a request.
Create the file `application/classes/controller/hello.php` in your application folder and fill it out like so:
Create the file `application/classes/Controller/Hello.php` in your application folder and fill it out like so:
<?php defined('SYSPATH') OR die('No Direct Script Access');
@@ -23,7 +23,7 @@ Create the file `application/classes/controller/hello.php` in your application f
Lets see what's going on here:
`<?php defined('SYSPATH') OR die('No Direct Script Access');`
: You should recognise the first tag as an opening php tag (if you don't you should probably [learn php](http://php.net)). What follows is a small check that makes sure that this file is being included by Kohana. It stops people from accessing files directly from the url.
: You should recognize the first tag as an opening php tag (if you don't you should probably [learn php](http://php.net)). What follows is a small check that makes sure that this file is being included by Kohana. It stops people from accessing files directly from the url.
`Class Controller_Hello extends Controller`
: This line declares our controller, each controller class has to be prefixed with `Controller_` and an underscore delimited path to the folder the controller is in (see [Conventions and styles](about.conventions) for more info). Each controller should also extend the base `Controller` class which provides a standard structure for controllers.

View File

@@ -0,0 +1,219 @@
# Importing Kohana as a Library
If you're working with an existing codebase it's often difficult to modernise the code as it would mean a complete rewrite and there's rarely the time. An alternative is to improve the codebase incrementally as best you can, gradually outsourcing code to external libraries to reduce the amount of old code there is to maintain.
This tutorial describes how to include the Kohana PHP framework into existing PHP applications, without having to use the routing and HMVC request handling features.
[!!] The code modified in this tutorial was copied from Kohana version 3.1.x. You may need to update it to work with future releases.
In normal usage of the Kohana framework, the `index.php` file acts as the request handler; it sets up the environment, loads the system configuration, and then handles the request (see [Request Flow](flow)).
We'll walk you through the steps required to create a file we'll call `include.php` which will allow you to include Kohana from exiting PHP applications.
## Demo application
The following file will serve as our (insultingly simple) demo application for this tutorial.
### File: `demo.php`
~~~
<?php
$content = 'Hello World';
?>
<html>
<head>
<title>Demo page</title>
</head>
<body>
<?php echo $content; ?>
</body>
</html>
~~~
## Install Kohana
[Download and install the Kohana framework](install); from this point on, we'll be referring to the location of the Kohana libraries as the `kohana` directory.
## Create a common setup file
Since `index.php` and `include.php` will duplicate a lot of code, we're going to move that code to a third file, `common.php`. The bulk of the code is unchanged; we've changed the install check to exit rather than return after rendering, and removed the request execution.
The new file creates the initial request object, rather than fully executing the request, so that, if you do define routes, the `Request::$initial` variable will be set up correctly.
### File: `kohana/common.php`
~~~
<?php
/**
* The directory in which your application specific resources are located.
* The application directory must contain the bootstrap.php file.
*
* @link http://kohanaframework.org/guide/about.install#application
*/
$application = 'application';
/**
* The directory in which your modules are located.
*
* @link http://kohanaframework.org/guide/about.install#modules
*/
$modules = 'modules';
/**
* The directory in which the Kohana resources are located. The system
* directory must contain the classes/kohana.php file.
*
* @link http://kohanaframework.org/guide/about.install#system
*/
$system = 'system';
/**
* The default extension of resource files. If you change this, all resources
* must be renamed to use the new extension.
*
* @link http://kohanaframework.org/guide/about.install#ext
*/
define('EXT', '.php');
/**
* Set the PHP error reporting level. If you set this in php.ini, you remove this.
* @link http://www.php.net/manual/errorfunc.configuration#ini.error-reporting
*
* When developing your application, it is highly recommended to enable notices
* and strict warnings. Enable them by using: E_ALL | E_STRICT
*
* In a production environment, it is safe to ignore notices and strict warnings.
* Disable them by using: E_ALL ^ E_NOTICE
*
* When using a legacy application with PHP >= 5.3, it is recommended to disable
* deprecated notices. Disable with: E_ALL & ~E_DEPRECATED
*/
error_reporting(E_ALL | E_STRICT);
/**
* End of standard configuration! Changing any of the code below should only be
* attempted by those with a working knowledge of Kohana internals.
*
* @link http://kohanaframework.org/guide/using.configuration
*/
// Set the full path to the docroot
define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);
// Make the application relative to the docroot, for symlink'd index.php
if ( ! is_dir($application) AND is_dir(DOCROOT.$application))
$application = DOCROOT.$application;
// Make the modules relative to the docroot, for symlink'd index.php
if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules))
$modules = DOCROOT.$modules;
// Make the system relative to the docroot, for symlink'd index.php
if ( ! is_dir($system) AND is_dir(DOCROOT.$system))
$system = DOCROOT.$system;
// Define the absolute paths for configured directories
define('APPPATH', realpath($application).DIRECTORY_SEPARATOR);
define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR);
define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR);
// Clean up the configuration vars
unset($application, $modules, $system);
if (file_exists('install'.EXT))
{
// Load the installation check
include 'install'.EXT;
exit; // Changes were made here
}
/**
* Define the start time of the application, used for profiling.
*/
if ( ! defined('KOHANA_START_TIME'))
{
define('KOHANA_START_TIME', microtime(TRUE));
}
/**
* Define the memory usage at the start of the application, used for profiling.
*/
if ( ! defined('KOHANA_START_MEMORY'))
{
define('KOHANA_START_MEMORY', memory_get_usage());
}
// Bootstrap the application
require APPPATH.'bootstrap'.EXT;
/**
* Instantiate the request object. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
Request::factory(); // Changes were made here
~~~
## Alter Kohana's `index.php`
Having moved most of the code from Kohana's `index.php` to `common.php` the new `kohana/index.php` contains only this:
### File: `kohana/index.php`
~~~
<?php
require_once 'common.php';
// Execute the request
Request::$initial->execute()
->execute()
->send_headers(TRUE)
->body();
~~~
## Create the include file
Our `include.php` file is also pretty simple. The try-catch clause is needed because if the request matches no routes Kohana will throw an `HTTP_Exception_404` exception.
### File: `kohana/include.php`
~~~
<?php
try {
require_once 'common.php';
}
catch (HTTP_Exception_404 $e)
{
// The request did not match any routes; ignore the 404 exception.
}
~~~
**NB:** Due to the way Kohana's routing works, if the request matches no routes it will fail to instantiate an object, and `Request::$current` and `Request::$initial` will not be available.
## Integration
Now that we're set up, we can add Kohana into our application using a single include, and then we're good to go.
### File: `demo.php`
~~~
<?php
require_once 'kohana/include.php';
$content = 'Hello World';
$content = HTML::anchor('http://kohanaframework.org/', $content);
?>
<html>
<head>
<title>Demo page</title>
</head>
<body>
<?php echo $content; ?>
<hr />
<?php echo URL::base(); ?>
<hr />
<?php echo Debug::dump(array(1,2,3,4,5)); ?>
</body>
</html>
~~~

View File

@@ -1,8 +1,8 @@
# Sharing Kohana
Because Kohana follows a [front controller] pattern, which means that all requests are sent to `index.php`, the filesystem is very configurable. Inside of `index.php` you can change the `$application`, `$modules`, and `$system` paths.
Kohana follows a [front controller pattern](http://en.wikipedia.org/wiki/Front_Controller_pattern "Front Controller pattern") (which means that all requests are sent to `index.php`) and as such the [filesystem](files) is very configurable. Inside of `index.php` you can change the `$application`, `$modules`, and `$system` paths.
[!!] There is a security check at the top of every Kohana file to prevent it from being accessed without using the front controller. Also, the `.htaccess` file should protect those folders as well. Moving the application, modules, and system directories to a location that cannot be accessed vie web can add another layer of security, but is optional.
[!!] There is a security check at the top of every Kohana file to prevent it from being accessed without using the front controller. Also, the `.htaccess` file should protect those folders as well. Moving the application, modules, and system directories to a location that cannot be accessed via the web can add another layer of security, but is optional.
The `$application` variable lets you set the directory that contains your application files. By default, this is `application`. The `$modules` variable lets you set the directory that contains module files. The `$system` variable lets you set the directory that contains the default Kohana files. You can move these three directories anywhere.
@@ -30,7 +30,7 @@ Then you would need to change the settings in `index.php` to be:
## Sharing system and modules
To take this a step further, we could point several kohana apps to the same system and modules folders. For example (and this is just an example, you could arrange these anyway you want):
To take this a step further, we could point several Kohana apps to the same system and modules folders. For example (and this is just an example, you could arrange these anyway you want):
apps/
foobar/
@@ -51,4 +51,4 @@ And you would need to change the settings in `index.php` to be:
$system = '../../../kohana/3.0.6';
$modules = '../../../kohana/modules';
Using this method each app can point to a central copy of kohana, and you can add a new version, and quickly update your apps to point to the new version by editing their `index.php` files.
With this method each app can point to a central copy of Kohana, and when you add a new version, allow you to quickly update the apps by editing their respective `index.php` files.

View File

@@ -1,93 +1,100 @@
# Migrating from 3.0.x
# Migrating from 3.2.x
## Request/Response
## HVMC Isolation
The request class has been split into a request and response class. To set the response body, you used to do:
HVMC Sub-request isolation has been improved to prevent exceptions leaking from this inner to the outer request. If you were previously catching any exceptions from sub-requests, you should now be checking the [Response] object returned from [Request::execute].
$this->request->response = 'foo';
## HTTP Exceptions
This has changed to:
The use of HTTP Exceptions is now encouraged over manually setting the [Response] status to, for example, '404'. This allows for easier custom error pages (detailed below);
$this->response->body('foo');
The full list of supported codes can be seen in the SYSPATH/classes/HTTP/Exception/ folder.
Some properties that existed in the request class have been converted into methods:
Syntax:
- Request::$controller -> Request::controller()
- Request::$action -> Request::action()
- Request::$directory -> Request::directory()
- Request::$uri -> Request::uri()
throw HTTP_Exception::factory($code, $message, array $variables, Exception $previous);
Request::instance() has been replaced by Request::current() and Request::initial(). Normally you'll want to use Request::current(), but if you are sure you want the *original* request (when running hmvc requests), use Request::initial().
Examples:
## Validation
// Page Not Found
throw HTTP_Exception::factory(404, 'The requested URL :uri was not found on this server.', array(
':uri' => $this->request->uri(),
));
The validation class has been improved to include "context" support. Because of this, the api has changed. Also, The class has been split: core validation logic is now separate from built-in validation rules. The new core class is called `Validation` and the rules are located in the `Valid` class.
// Unauthorized / Login Requied
throw HTTP_Exception::factory(401)->authenticate('Basic realm="MySite"');
### Context
// Forbidden / Permission Deined
throw HTTP_Exception::factory(403);
The validation class now contains "context" support. This allowed us to merge the rule() and callback() methods, and there is now simply a rule() method that handles both cases.
## Redirects (HTTP 300, 301, 302, 303, 307)
Old usage:
Redirects are no longer issued against the [Request] object. The new syntax from inside a controller is:
rule('password', 'matches', array('repeat_password'))
$this->redirect('http://www.google.com', 302);
New usage:
or from outside a controller:
rule('password', 'matches', array(':validation', 'password', 'repeat_password'))
HTTP::redirect('http://www.google.com', 302);
The third parameter now contains all parameters that get passed to the rule. If you look at Valid::matches(), you'll see:
## Custom Error Pages (HTTP 500, 404, 403, 401 etc)
public static function matches($array, $field, $match)
Custom error pages are now easier than ever to implement, thanks to some of the changes brought about by the HVMC and Redirect changes above.
:validation is the first parameter, 'password' is the second (the field we want to check) and 'repeat_password' is the third (what we want to match)
See [Custom Error Pages](tutorials/error-pages) for more details.
:validation is a special "context" variable that tells the Validation class to replace with the actual validation class. You can see in this way, the matches rule is no different than how callbacks used to work, yet are more powerful. There are other context variables:
## Browser cache checking (ETags)
- :validation - The validation object
- :field - The field being compared (rule('username', 'min_length', array(':field', 4)))
- :value - The value of the field
The Response::check_cache method has moved to [HTTP::check_cache], with an alias at [Controller::check_cache]. Previously, this method would be used from a controller like this:
You can use any php function as a rule if it returns a boolean value.
$this->response->check_cache(sha1('my content'), Request $this->request);
### Filters
Now, there are two options for using the method:
Filters have been removed from the validation class. There is no specific replacement. If you were using it with ORM, there is a new mechanism for filtering in that module.
$this->check_cache(sha1('my content'));
## Cookie salts
which is an alias for:
The cookie class now throws an exception if there isn't a salt set, and no salt is the now the default. You'll need to make sure you set the salt in your bootstrap:
HTTP::check_cache($this->request, $this->response, sha1('my content'));
Cookie::$salt = 'foobar';
## PSR-0 support (file/class naming conventions)
Or define an extended cookie class in your application:
With the introduction of [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) support, the autoloading of classes is case sensitive. Now, the file (and folder) names must match the class name exactly.
class Cookie extends Kohana_Cookie
{
public static $salt = 'foobar';
}
Examples:
## Controller constructor
Kohana_Core
If for some reason you are overloading your controller's constructor, it has changed to:
would be located in
public function __construct(Request $request, Response $response)
classes/Kohana/Core.php
## index.php / bootstrap.php changes
and
The main change here is that the request execution has been removed from bootstrap.php and moved to index.php. This allows you to use one bootstrap when doing testing. The reason for this change is that the bootstrap should only setup the environment. It shouldn't run it.
Kohana_HTTP_Header
## 404 Handling
would be located in
Kohana now has built in exception support for 404 and other http status codes. If you were using ReflectionException to detect 404s, you should be using HTTP_Exception_404 instead. For details on how to handle 404s, see [error handling](errors).
classes/Kohana/HTTP/Header.php
## Form Class
This also affects dynamically named classes such as drivers and ORMs. So for example, in the database config using `'mysql'` as the type instead of `'MySQL'` would throw a class not found error.
If you used Form::open(), the default behavior has changed. It used to default to the current URI, but now an empty parameter will default to "/" (your home page).
## Query Builder Identifier Escaping
## Logging
The query builder will no longer detect columns like `COUNT("*")`. Instead, you will need to use `DB::expr()` any time you need an unescaped column. For example:
The log message level constants now belong to the Log class. If you are referencing those constants to invoke Kohana::$log->add( ... ) you will need to change the following:
DB::select(DB::expr('COUNT(*)'))->from('users')->execute()
- Kohana::ERROR -> Log::ERROR
- Kohana::DEBUG -> Log::DEBUG
- Kohana::INFO -> Log::INFO
## Route Filters
In `3.3.0`, you can no longer pass a callback to `Route::uri()`. Instead, we've added the ability to define one or more filters which will be able to decide if the route matches and will also allow you to change any of the parameters. These filters will receive the `Route` object being tested, the currently matched `$params` array, and the `Request` object as the three parameters.
Route::set('route-name', 'some/uri/<id>')
->filter(function($route, $params, $request) {
// Returning FALSE will make this route not match
// Returning an array will replace the $params sent to the controller
});
These filters can be used for things like prepending the request method to the action, checking if a resource exists before matching the route, or any other logic that the URI alone cannot provide. You can add as many filters as needed so it's useful to keep filters as small as possible to make them reusable.
See [Routing](routing#route-filters) for more details.