Upgrade to KH 3.1.3.1

This commit is contained in:
Deon George
2011-05-13 16:00:25 +10:00
parent 8013aadc4c
commit 6d256839fc
675 changed files with 22771 additions and 24111 deletions

View File

@@ -0,0 +1,69 @@
# 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:
Cookie::set('mycookie', 'any string value');
Or to load an [Encrypt] instance, just call [Encrypt::instance]:
$encrypt = Encrypt::instance();
Classes are loaded via the [Kohana::auto_load] method, which makes a simple conversion from class name to file name:
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
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)
## Custom Autoloaders
Kohana's default autoloader is enabled in `application/bootstrap.php` using [spl_autoload_register](http://php.net/spl_autoload_register):
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.
### Example: Zend
You can easily gain access to other libraries if they include an autoloader. For example, here is how to enable Zend's autoloader so you can use Zend libraries in your Kohana application.
#### Download and install the Zend Framework files
- [Download the latest Zend Framework files](http://framework.zend.com/download/latest).
- Create a `vendor` directory at `application/vendor`. This keeps third party software separate from your application classes.
- Move the decompressed Zend folder containing Zend Framework to `application/vendor/Zend`.
#### Include Zend's Autoloader in your bootstrap
Somewhere in `application/bootstrap.php`, copy the following code:
/**
* Enable Zend Framework autoloading
*/
if ($path = Kohana::find_file('vendor', 'Zend/Loader'))
{
ini_set('include_path',
ini_get('include_path').PATH_SEPARATOR.dirname(dirname($path)));
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
}
#### Usage example
You can now autoload any Zend Framework classes from inside your Kohana application.
if ($validate($_POST))
{
$mailer = new Zend_Mail;
$mailer->setBodyHtml($view)
->setFrom(Kohana::config('site')->email_from)
->addTo($email)
->setSubject($message)
->send();
}

View File

@@ -0,0 +1,111 @@
# Bootstrap
The bootstrap is located at `application/bootstrap.php`. It is responsible for setting up the Kohana environment and executing the main response. It is included by `index.php` (see [Request flow](flow))
[!!] The bootstrap is responsible for the flow of your application. In previous versions of Kohana the bootstrap was in `system` and was somewhat of an unseen, uneditible force. In Kohana 3 the bootstrap takes on a much more integral and versatile role. Do not be afraid to edit and change your bootstrap however you see fit.
## 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.
~~~
// 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.
~~~
// Sample excerpt from bootstrap.php with comments trimmed down
Kohana::init(array('
base_url' => '/kohana/',
index_file => false,
));
// Attach the file writer to logging. Multiple writers are supported.
Kohana::$log->attach(new Kohana_Log_File(APPPATH.'logs'));
// Attach a file reader to config. Multiple readers are supported.
Kohana::$config->attach(new Kohana_Config_File);
~~~
You can add conditional statements to make the bootstrap have different values based on certain settings. For example, detect whether we are live by checking `$_SERVER['HTTP_HOST']` and set caching, profiling, etc. accordingly. This is just an example, there are many different ways to accomplish the same thing.
~~~
// Excerpt from http://github.com/isaiahdw/kohanaphp.com/blob/f2afe8e28b/application/bootstrap.php
... [trimmed]
/**
* Set the environment status by the domain.
*/
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]
*/
Kohana::init(array(
'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaphp.com/',
'caching' => Kohana::$environment === Kohana::PRODUCTION,
'profile' => Kohana::$environment !== Kohana::PRODUCTION,
'index_file' => FALSE,
));
... [trimmed]
~~~
[!!] Note: The default bootstrap will set `Kohana::$environment = $_ENV['KOHANA_ENV']` if set. Docs on how to supply this variable are available in your web server's documentation (e.g. [Apache](http://httpd.apache.org/docs/1.3/mod/mod_env.html#setenv), [Lighttpd](http://redmine.lighttpd.net/wiki/1/Docs:ModSetEnv#Options)). This is considered better practice than many alternative methods to set `Kohana::$enviroment`, as you can change the setting per server, without having to rely on config options or hostnames.
## Modules
**Read the [Modules](modules) page for a more detailed description.**
[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.
~~~
// Example excerpt from bootstrap.php
Kohana::modules(array(
'database' => MODPATH.'database',
'orm' => MODPATH.'orm',
'userguide' => MODPATH.'userguide',
));
~~~
## Routes
**Read the [Routing](routing) page for a more detailed description and more examples.**
[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',
'action' => 'index',
));
~~~

View File

@@ -0,0 +1 @@
This will discuss controller basics, like before() and after(), private function, and about extending controllers like the Controller_Template, or using a parent::before() for authentication.

View File

@@ -0,0 +1,417 @@
# Conventions and Coding Style
It is encouraged that you follow Kohana's coding style. This makes code more readable and allows for easier code sharing and contributing.
## Class Names and File Location
Class names in Kohana follow a strict convention to facilitate [autoloading](autoloading). Class names should have uppercase first letters with underscores to separate words. Underscores are significant as they directly reflect the file location in the filesystem.
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.
3. All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](files).
### Examples {#class-name-examples}
Remember that in a class, an underscore means a new directory. Consider the following examples:
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
## Coding Standards
In order to produce highly consistent source code, we ask that everyone follow the coding standards as closely as possible.
### Brackets
Please use [BSD/Allman Style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracketing.
#### Curly Brackets
Curly brackets are placed on their own line, indented to the same level as the control statement.
// Correct
if ($a === $b)
{
...
}
else
{
...
}
// Incorrect
if ($a === $b) {
...
} else {
...
}
#### Class Brackets
The only exception to the curly bracket rule is, the opening bracket of a class goes on the same line.
// Correct
class Foo {
// Incorrect
class Foo
{
#### Empty Brackets
Don't put any characters inside empty brackets.
// Correct
class Foo {}
// Incorrect
class Foo { }
#### Array Brackets
Arrays may be single line or multi-line.
array('a' => 'b', 'c' => 'd')
array(
'a' => 'b',
'c' => 'd',
)
##### Opening Parenthesis
The opening array parenthesis goes on the same line.
// Correct
array(
...
)
// Incorrect:
array
(
...
)
##### Closing parenthesis
###### Single Dimension
The closing parenthesis of a multi-line single dimension array is placed on its own line, indented to the same level as the assignment or statement.
// Correct
$array = array(
...
)
// Incorrect
$array = array(
...
)
###### Multidimensional
The nested array is indented one tab to the right, following the single dimension rules.
// Correct
array(
'arr' => array(
...
),
'arr' => array(
...
),
)
array(
'arr' => array(...),
'arr' => array(...),
)
##### Arrays as Function Arguments
// Correct
do(array(
...
))
// Incorrect
do(array(
...
))
As noted at the start of the array bracket section, single line syntax is also valid.
// Correct
do(array(...))
// Alternative for wrapping long lines
do($bar, 'this is a very long line',
array(...));
### Naming Conventions
Kohana uses under_score naming, not camelCase naming.
#### Classes
// Controller class, uses Controller_ prefix
class Controller_Apple extends Controller {
// Model class, uses Model_ prefix
class Model_Cheese extends Model {
// Regular class
class Peanut {
When creating an instance of a class, don't use parentheses if you're not passing something on to the constructor:
// Correct:
$db = new Database;
// Incorrect:
$db = new Database();
#### Functions and Methods
Functions should be all lowercase, and use under_scores to separate words:
function drink_beverage($beverage)
{
#### Variables
All variables should be lowercase and use under_score, not camelCase:
// Correct:
$foo = 'bar';
$long_example = 'uses underscores';
// Incorrect:
$weDontWantThis = 'understood?';
### Indentation
You must use tabs to indent your code. Using spaces for tabbing is strictly forbidden.
Vertical spacing (for multi-line) is done with spaces. Tabs are not good for vertical alignment because different people have different tab widths.
$text = 'this is a long text block that is wrapped. Normally, we aim for '
.'wrapping at 80 chars. Vertical alignment is very important for '
.'code readability. Remember that all indentation is done with tabs,'
.'but vertical alignment should be completed with spaces, after '
.'indenting with tabs.';
### String Concatenation
Do not put spaces around the concatenation operator:
// Correct:
$str = 'one'.$var.'two';
// Incorrect:
$str = 'one'. $var .'two';
$str = 'one' . $var . 'two';
### Single Line Statements
Single-line IF statements should only be used when breaking normal execution (e.g. return or continue):
// Acceptable:
if ($foo == $bar)
return $foo;
if ($foo == $bar)
continue;
if ($foo == $bar)
break;
if ($foo == $bar)
throw new Exception('You screwed up!');
// Not acceptable:
if ($baz == $bun)
$baz = $bar + 2;
### Comparison Operations
Please use OR and AND for comparison:
// Correct:
if (($foo AND $bar) OR ($b AND $c))
// Incorrect:
if (($foo && $bar) || ($b && $c))
Please use elseif, not else if:
// Correct:
elseif ($bar)
// Incorrect:
else if($bar)
### Switch Structures
Each case, break and default should be on a separate line. The block inside a case or default must be indented by 1 tab.
switch ($var)
{
case 'bar':
case 'foo':
echo 'hello';
break;
case 1:
echo 'one';
break;
default:
echo 'bye';
break;
}
### Parentheses
There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis.
// Correct:
if ($foo == $bar)
if ( ! $foo)
// Incorrect:
if($foo == $bar)
if(!$foo)
if ((int) $foo)
if ( $foo == $bar )
if (! $foo)
### Ternaries
All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables.
$foo = ($bar == $foo) ? $foo : $bar;
$foo = $bar ? $foo : $bar;
All comparisons and operations must be done inside of a parentheses group:
$foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
When separating complex ternaries (ternaries where the first part goes beyond ~80 chars) into multiple lines, spaces should be used to line up operators, which should be at the front of the successive lines:
$foo = ($bar == $foo)
? $foo
: $bar;
### Type Casting
Type casting should be done with spaces on each side of the cast:
// Correct:
$foo = (string) $bar;
if ( (string) $bar)
// Incorrect:
$foo = (string)$bar;
When possible, please use type casting instead of ternary operations:
// Correct:
$foo = (bool) $bar;
// Incorrect:
$foo = ($bar == TRUE) ? TRUE : FALSE;
When casting type to integer or boolean, use the short format:
// Correct:
$foo = (int) $bar;
$foo = (bool) $bar;
// Incorrect:
$foo = (integer) $bar;
$foo = (boolean) $bar;
### Constants
Always use uppercase for constants:
// Correct:
define('MY_CONSTANT', 'my_value');
$a = TRUE;
$b = NULL;
// Incorrect:
define('MyConstant', 'my_value');
$a = True;
$b = null;
Place constant comparisons at the end of tests:
// Correct:
if ($foo !== FALSE)
// Incorrect:
if (FALSE !== $foo)
This is a slightly controversial choice, so I will explain the reasoning. If we were to write the previous example in plain English, the correct example would read:
if variable $foo is not exactly FALSE
And the incorrect example would read:
if FALSE is not exactly variable $foo
Since we are reading left to right, it simply doesn't make sense to put the constant first.
### Comments
#### One-line Comments
Use //, preferably above the line of code you're commenting on. Leave a space after it and start with a capital. Never use #.
// Correct
//Incorrect
// incorrect
# Incorrect
### Regular Expressions
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)
// Incorrect:
if (eregi('abc', $str))
Use single quotes around your regular expressions rather than double quotes. Single-quoted strings are more convenient because of their simplicity. Unlike double-quoted strings they don't support variable interpolation nor integrated backslash sequences like \n or \t, etc.
// Correct:
preg_match('/abc/', $str);
// Incorrect:
preg_match("/abc/", $str);
When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over \\n.
// Correct:
preg_replace('/(\d+) dollar/', '$1 euro', $str);
// Incorrect:
preg_replace('/(\d+) dollar/', '\\1 euro', $str);
Finally, please note that the $ character for matching the position at the end of the line allows for a following newline character. Use the D modifier to fix this if needed. [More info](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
$str = "email@example.com\n";
preg_match('/^.+@.+$/', $str); // TRUE
preg_match('/^.+@.+$/D', $str); // FALSE

View File

@@ -0,0 +1,100 @@
# Cookies
Kohana provides classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same functionality. They allow the developer to store temporary or persistent information about a specific client for later retrieval, usually to make something persistent between requests.
[Cookies](http://en.wikipedia.org/wiki/HTTP_cookie) should be used for storing non-private data that is persistent for a long period of time. For example storing a user preference or a language setting. Use the [Cookie] class for getting and setting cookies.
[!!] Kohana uses "signed" cookies. Every cookie that is stored is combined with a secure hash to prevent modification of the cookie. If a cookie is modified outside of Kohana the hash will be incorrect and the cookie will be deleted. This hash is generated using [Cookie::salt()], which uses the [Cookie::$salt] property. You must define this setting in your bootstrap.php:
Cookie::$salt = 'foobar';
Or define an extended cookie class in your application:
class Cookie extends Kohana_Cookie
{
public static $salt = 'foobar';
}
You should set the salt to a secure value. The example above is only for demonstrative purposes.
Nothing stops you from using `$_COOKIE` like normal, but you can not mix using the Cookie class and the regular `$_COOKIE` global, because the hash that Kohana uses to sign cookies will not be present, and Kohana will delete the cookie.
## Storing, Retrieving, and Deleting Data
[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class.
### Storing Data
Storing session or cookie data is done using the [Cookie::set] method:
// Set cookie data
Cookie::set($key, $value);
// Store a user id
Cookie::set('user_id', 10);
### Retrieving Data
Getting session or cookie data is done using the [Cookie::get] method:
// Get cookie data
$data = Cookie::get($key, $default_value);
// Get the user id
$user = Cookie::get('user_id');
### Deleting Data
Deleting session or cookie data is done using the [Cookie::delete] method:
// Delete cookie data
Cookie::delete($key);
// Delete the user id
Cookie::delete('user_id');
## Cookie Settings
All of the cookie settings are changed using static properties. You can either change these settings in `bootstrap.php` or by using [transparent extension](extension). Always check these settings before making your application live, as many of them will have a direct affect on the security of your application.
The most important setting is [Cookie::$salt], which is used for secure signing. This value should be changed and kept secret:
Cookie::$salt = 'your secret is safe with me';
[!!] Changing this value will render all cookies that have been set before invalid.
By default, cookies are stored until the browser is closed. To use a specific lifetime, change the [Cookie::$expiration] setting:
// Set cookies to expire after 1 week
Cookie::$expiration = 604800;
// Alternative to using raw integers, for better clarity
Cookie::$expiration = Date::WEEK;
The path that the cookie can be accessed from can be restricted using the [Cookie::$path] setting.
// Allow cookies only when going to /public/*
Cookie::$path = '/public/';
The domain that the cookie can be accessed from can also be restricted, using the [Cookie::$domain] setting.
// Allow cookies only on the domain www.example.com
Cookie::$domain = 'www.example.com';
If you want to make the cookie accessible on all subdomains, use a dot at the beginning of the domain.
// Allow cookies to be accessed on example.com and *.example.com
Cookie::$domain = '.example.com';
To only allow the cookie to be accessed over a secure (HTTPS) connection, use the [Cookie::$secure] setting.
// Allow cookies to be accessed only on a secure connection
Cookie::$secure = TRUE;
// Allow cookies to be accessed on any connection
Cookie::$secure = FALSE;
To prevent cookies from being accessed using Javascript, you can change the [Cookie::$httponly] setting.
// Make cookies inaccessible to Javascript
Cookie::$httponly = TRUE;

View File

@@ -0,0 +1,20 @@
# Debugging
Kohana includes several tools to help you debug your application.
The most basic of these is [Debug::vars]. This simple method will display any number of variables, similar to [var_export](http://php.net/var_export) or [print_r](http://php.net/print_r), but using HTML for extra formatting.
// Display a dump of the $foo and $bar variables
echo Debug::vars($foo, $bar);
Kohana also provides a method to show the source code of a particular file using [Debug::source].
// Display this line of source code
echo Debug::source(__FILE__, __LINE__);
If you want to display information about your application files without exposing the installation directory, you can use [Debug::path]:
// Displays "APPPATH/cache" rather than the real path
echo Debug::path(APPPATH.'cache');
If you are having trouble getting something to work correctly, you could check your Kohana logs and your webserver logs, as well as using a debugging tool like [Xdebug](http://www.xdebug.org/).

View File

@@ -0,0 +1,76 @@
# Error/Exception Handling
Kohana provides both an exception handler and an error handler that transforms errors into exceptions using PHP's [ErrorException](http://php.net/errorexception) class. Many details of the error and the internal state of the application is displayed by the handler:
1. Exception class
2. Error level
3. Error message
4. Source of the error, with the error line highlighted
5. A [debug backtrace](http://php.net/debug_backtrace) of the execution flow
6. Included files, loaded extensions, and global variables
## Example
Click any of the links to toggle the display of additional information:
<div>{{userguide/examples/error}}</div>
## Disabling Error/Exception Handling
If you do not want to use the internal error handling, you can disable it (highly discouraged) when calling [Kohana::init]:
Kohana::init(array('errors' => FALSE));
## Error Reporting
By default, Kohana displays all errors, including strict mode warnings. This is set using [error_reporting](http://php.net/error_reporting):
error_reporting(E_ALL | E_STRICT);
When you application is live and in production, a more conservative setting is recommended, such as ignoring notices:
error_reporting(E_ALL & ~E_NOTICE);
If you get a white screen when an error is triggered, your host probably has disabled displaying errors. You can turn it on again by adding this line just after your `error_reporting` call:
ini_set('display_errors', TRUE);
Errors should **always** be displayed, even in production, because it allows you to use [exception and error handling](debugging.errors) to serve a nice error page rather than a blank white screen when an error happens.
## HTTP Exception Handling
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!');
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*:
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;
}
}
}
And put something like this in your bootstrap to register the handler.
set_exception_handler(array('Foobar_Exception_Handler', 'handle'));
> *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/*.

View File

@@ -0,0 +1,101 @@
# Transparent Class Extension
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.
[!!] 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:
<?php defined('SYSPATH') or die('No direct script access.');
class Cookie extends Kohana_Cookie {
/**
* @var mixed default encryption instance
*/
public static $encryption = 'default';
/**
* Sets an encrypted cookie.
*
* @uses Cookie::set
* @uses Encrypt::encode
*/
public static function encrypt($name, $value, $expiration = NULL)
{
$value = Encrypt::instance(Cookie::$encrpytion)->encode((string) $value);
parent::set($name, $value, $expiration);
}
/**
* Gets an encrypted cookie.
*
* @uses Cookie::get
* @uses Encrypt::decode
*/
public static function decrypt($name, $default = NULL)
{
if ($value = parent::get($name, NULL))
{
$value = Encrypt::instance(Cookie::$encryption)->decode($value);
}
return isset($value) ? $value : $default;
}
} // End Cookie
Now calling `Cookie::encrypt('secret', $data)` will create an encrypted cookie which we can decrypt with `$data = Cookie::decrypt('secret')`.
## 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`.
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`).
class Cookie extends Kohana_Cookie {
// Set a new salt
public $salt = "some new better random salt phrase";
// Don't allow javascript access to cookies
public $httponly = TRUE;
}
## Example: TODO: an example
Just post the code and brief description of what function it adds, you don't have to do the "How it works" like above.
## Example: TODO: something else
Just post the code and brief description of what function it adds, you don't have to do the "How it works" like above.
## More examples
TODO: Provide some links to modules on github, etc that have examples of transparent extension in use.
## 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`:
class Encrypted_Cookie extends Kohana_Cookie {
// Use the same encrypt() and decrypt() methods as above
}
And create `MODPATH/mymod/cookie.php`:
class Cookie extends Encrypted_Cookie {}
This will still allow users to add their own extension to [Cookie] while leaving your extensions intact. To do that they would make a cookie class that extends `Encrypted_Cookie` (rather than `Kohana_Cookie`) in their application folder.

View File

@@ -0,0 +1,83 @@
# Cascading Filesystem
The Kohana filesystem is a hierarchy of similar directory structures that cascade. The hierarchy in Kohana (used when a file is loaded by [Kohana::find_file]) is in the following order:
1. **Application Path**
Defined as `APPPATH` in `index.php`. The default value is `application`.
2. **Module Paths**
This is set as an associative array using [Kohana::modules] in `APPPATH/bootstrap.php`. Each of the values of the array will be searched **in the order that the modules are defined**.
3. **System Path**
Defined as `SYSPATH` in `index.php`. The default value is `system`. All of the main or "core" files and classes are defined here.
Files that are in directories higher up the include path order take precedence over files of the same name lower down the order, which makes it is possible to overload any file by placing a file with the same name in a "higher" directory:
![Cascading Filesystem Infographic](cascading_filesystem.png)
This image is only shows certain files, but we can use it to illustrate some examples of the cascading filesystem:
* If Kohana catches an error, it would display the `kohana/error.php` view, So it would call `Kohana::find_file('views', 'kohana/error')`. This would return `application/views/kohana/error.php` because it takes precidence over `system/views/kohana/error.php`. By doing this we can change the error view without editing the system folder.
* 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 you used `View::factory('user')` it would call `Kohana::find_file('views','user')` which would return `modules/common/views/user.php`.
* If we wanted to change something in `config/database.php` we could copy the file to `application/config/database.php` and make the changes there. Keep in mind that [config files are merged](files/config#merge) rather than overwritten by the cascade.
## Types of Files
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).
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.
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.
messages/
: Message files return an associative array of strings that can be loaded using [Kohana::message]. Messages and i18n files differ in that messages are not translated, but always written in the default language and referred to by a single key. Message files are merged rather than overwritten by the cascade. See [message files](files/messages) for more information.
views/
: Views are plain PHP files which are used to generate HTML or other output. The view file is loaded into a [View] object and assigned variables, which it then converts into an HTML fragment. Multiple views can be used within each other. See [views](mvc/views) for more information.
*other*
: You can include any other folders in your cascading filesystem. Examples include, but are not limited to, `guide`, `vendor`, `media`, whatever you want. For example, to find `media/logo.png` in the cascading filesystem you would call `Kohana::find_file('media','logo','png')`.
## Finding Files
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 "views/user/login.php"
$path = Kohana::find_file('views', 'user/login');
If the file doesn't have a `.php` extension, pass the extension as the third param.
// Find the full path to "guide/menu.md"
$path = Kohana::find_file('guide', 'menu', 'md');
// If $name is "2000-01-01-first-post" this would look for "posts/2000-01-01-first-post.textile"
$path = Kohana::find_file('posts', $name, '.textile');
## Vendor Extensions
We call extensions or external libraries that are not specific to Kohana "vendor" extensions, and they go in the vendor folder, either in application or in a module. Because these libraries do not follow Kohana's file naming conventions, they cannot be autoloaded by Kohana, so you will have to manually included them. Some examples of vendor libraries are [Markdown](http://daringfireball.net/projects/markdown/), [DOMPDF](http://code.google.com/p/dompdf), [Mustache](http://github.com/bobthecow/mustache.php) and [Swiftmailer](http://swiftmailer.org/).
For example, if you wanted to use [DOMPDF](http://code.google.com/p/dompdf), you would copy it to `application/vendor/dompdf` and include the DOMPDF autoloading class. It can be useful to do this in a controller's before method, as part of a module's init.php, or the contstructor of a singleton class.
require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config','inc');
Now you can use DOMPDF without loading any more files:
$pdf = new DOMPDF;
[!!] If you want to convert views into PDFs using DOMPDF, try the [PDFView](http://github.com/shadowhand/pdfview) module.

View File

@@ -0,0 +1,41 @@
# Classes
TODO: Brief intro to classes.
[Models](mvc/models) and [Controllers](mvc/controllers) are classes as well, but are treated slightly differently by Kohana. Read their respective pages to learn more.
## Helper or Library?
Kohana 3 does not differentiate between "helper" classes and "library" classes like in previous versions. They are all placed in the `classes/` folder and follow the same conventions. The distinction is that in general, a "helper" class is used statically, (for examples see the [helpers included in Kohana](helpers)), and library classes are typically instantiated and used as objects (like the [Database query builders](../database/query/builder)). The distinction is not black and white, and is irrelevant anyways, since they are treated the same by Kohana.
## Creating a class
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
class Foobar {
static function magic() {
// Does something
}
}
We can now call `Foobar::magic()` any where and Kohana will [autoload](autoloading) the file for us.
We can also put classes in subdirectories.
// classes/professor/baxter.php
class Professor_Baxter {
static function teach() {
// Does something
}
}
We could now call `Professor_Baxter::teach()` any where we want.
For examples of how to create and use classes, simply look at the 'classes' folder in `system` or any module.
## Namespacing your classes
TODO: Discuss namespacing to provide transparent extension functionality in your own classes/modules.

View File

@@ -0,0 +1,102 @@
# Config Files
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.');
return array(
'setting' => 'value',
'options' => array(
'foo' => 'bar',
),
);
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'];
## Merge
Configuration files are slightly different from most other files within the [cascading filesystem](files) in that they are **merged** rather than overloaded. This means that all configuration files with the same file path are combined to produce the final configuration. The end result is that you can overload *individual* settings rather than duplicating an entire file.
For example, if we wanted to change or add to an entry in the inflector configuration file, we would not need to duplicate all the other entries from the default configuration file.
// config/inflector.php
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'irregular' => array(
'die' => 'dice', // does not exist in default config file
'mouse' => 'mouses', // overrides 'mouse' => 'mice' in the default config file
);
## Creating your own config files
Let's say we want a config file to store and easily change things like the title of a website, or the google analytics code. We would create a config file, let's call it `site.php`:
// config/site.php
<?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.
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.');
return array(
'1.0.0' => array(
'codename' => 'Frog',
'download' => 'files/ourapp-1.0.0.tar.gz',
'documentation' => 'docs/1.0.0',
'released' => '06/05/2009',
'issues' => 'link/to/bug/tracker',
),
'1.1.0' => array(
'codename' => 'Lizard',
'download' => 'files/ourapp-1.1.0.tar.gz',
'documentation' => 'docs/1.1.0',
'released' => '10/15/2009',
'issues' => 'link/to/bug/tracker',
),
/// ... etc ...
);
You could then do the following:
// In your controller
$view->versions = Kohana::config('versions');
// In your view:
foreach ($versions as $version)
{
// echo some html to display each version
}

View File

@@ -0,0 +1,67 @@
# I18n
Kohana has a fairly simple and easy to use i18n system. It is slightly modeled after gettext, but is not as featureful. If you need the features of gettext, please use that :)
## __()
Kohana has a __() function to do your translations for you. This function is only meant for small sections of text, not entire paragraphs or pages of translated text.
To echo a translated string:
<?php echo __('Hello, world!');?>
This will echo 'Home' unless you've changed the defined language, which is explained below.
## Changing the displayed language
Use the I18n::lang() method to change the displayed language:
I18n::lang('fr');
This will change the language to 'es-es'.
## Defining language files
To define the language file for the above language change, create a `i18n/fr.php` that contains:
<?php
return array
(
'Hello, world!' => 'Bonjour, monde!',
);
Now when you do `__('Hello, world!')`, you will get `Bonjour, monde!`
## I18n variables
You can define variables in your __() calls like so:
echo __('Hello, :user', array(':user' => $username));
Your i18n key in your translation file will need to be defined as:
<?php
return array
(
'Hello, :user' => 'Bonjour, :user',
);
## Defining your own __() function
You can define your own __() function by simply defining your own i18n class:
<?php
class I18n extends Kohana_I18n
{
// Intentionally empty
}
function __($string, array $values = NULL, $lang = 'en-us')
{
// Your functionality here
}
This will cause the built-in __() function to be ignored.

View File

@@ -0,0 +1,36 @@
# Messages
Kohana has a robust key based lookup system so you can define system messages.
## Getting a message
Use the Kohana::message() method to get a message key:
Kohana::message('forms', 'foobar');
This will look in the `messages/forms.php` file for the `foobar` key:
<?php
return array(
'foobar' => 'Hello, world!',
);
You can also look in subfolders and sub-keys:
Kohana::message('forms/contact', 'foobar.bar');
This will look in the `messages/forms/contact.php` for the `[foobar][bar]` key:
<?php
return array(
'foobar' => array(
'bar' => 'Hello, world!',
),
);
## 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.

View File

@@ -0,0 +1,27 @@
# Request Flow
Every application follows the same flow:
1. Application starts from `index.php`.
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.
2. Once we are in `bootstrap.php`:
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.
* Module paths are added to the [cascading filesystem](files).
* Includes each module's `init.php` file, if it exists.
* The `init.php` file can perform additional environment setup, including adding routes.
10. [Route::set] is called multiple times to define the [application routes](routing).
11. [Request::instance] is called to start processing the request.
1. Checks each route that has been set until a match is found.
2. Creates the controller instance and passes the request to it.
3. Calls the [Controller::before] method.
4. Calls the controller action, which generates the request response.
5. Calls the [Controller::after] method.
* The above 5 steps can be repeated multiple times when using [HMVC sub-requests](requests).
3. Application flow returns to index.php
12. The main [Request] response is displayed

View File

@@ -0,0 +1,135 @@
# Fragments
Fragments are a quick and simple way to cache HTML or other output. Fragments are not useful for caching objects or raw database results, in which case you should use a more robust caching method, which can be achieved with the [Cache module](../cache). Fragments use [Kohana::cache()] and will be placed in the cache directory (`application/cache` by default).
You should use Fragment (or any caching solution) when reading the cache is faster than reprocessing the result. Reading and parsing a remote file, parsing a complicated template, calculating something, etc.
Fragments are typically used in view files.
## Usage
Fragments are used by calling [Fragment::load()] in an `if` statement at the beginning of what you want cached, and [Fragment::save()] at the end. They use [output buffering](http://www.php.net/manual/en/function.ob-start.php) to capture the output between the two function calls.
You can specify the lifetime (in seconds) of the Fragment using the second parameter of [Fragment::load()]. The default lifetime is 30 seconds. You can use the [Date] helper to make more readable times.
Fragments will store a different cache for each language (using [I18n]) if you pass `true` as the third parameter to [Fragment::load()];
You can force the deletion of a Fragment using [Fragment::delete()], or specify a lifetime of 0.
~~~
// Cache for 5 minutes, and cache each language
if ( ! Fragment::load('foobar', Date::MINUTE * 5, true))
{
// Anything that is echo'ed here will be saved
Fragment::save();
}
~~~
## Example: Calculating Pi
In this example we will calculate pi to 1000 places, and cache the result using a fragment. The first time you run this it will probably take a few seconds, but subsequent loads will be much faster, until the fragment lifetime runs out.
~~~
if ( ! Fragment::load('pi1000', Date::HOUR * 4))
{
// Change function nesting limit
ini_set('xdebug.max_nesting_level',1000);
// Source: http://mgccl.com/2007/01/22/php-calculate-pi-revisited
function bcfact($n)
{
return ($n == 0 || $n== 1) ? 1 : bcmul($n,bcfact($n-1));
}
function bcpi($precision)
{
$num = 0;$k = 0;
bcscale($precision+3);
$limit = ($precision+3)/14;
while($k < $limit)
{
$num = bcadd($num, bcdiv(bcmul(bcadd('13591409',bcmul('545140134', $k)),bcmul(bcpow(-1, $k), bcfact(6*$k))),bcmul(bcmul(bcpow('640320',3*$k+1),bcsqrt('640320')), bcmul(bcfact(3*$k), bcpow(bcfact($k),3)))));
++$k;
}
return bcdiv(1,(bcmul(12,($num))),$precision);
}
echo bcpi(1000);
Fragment::save();
}
echo View::factory('profiler/stats');
?>
~~~
## Example: Recent Wikipedia edits
In this example we will use the [Feed] class to retrieve and parse an RSS feed of recent edits to [http://en.wikipedia.org](http://en.wikipedia.org), then use Fragment to cache the results.
~~~
$feed = "http://en.wikipedia.org/w/index.php?title=Special:RecentChanges&feed=rss";
$limit = 50;
// Displayed feeds are cached for 30 seconds (default)
if ( ! Fragment::load('rss:'.$feed)):
// Parse the feed
$items = Feed::parse($feed, $limit);
foreach ($items as $item):
// Convert $item to object
$item = (object) $item;
echo HTML::anchor($item->link,$item->title);
?>
<blockquote>
<p>author: <?php echo $item->creator ?></p>
<p>date: <?php echo $item->pubDate ?></p>
</blockquote>
<?php
endforeach;
Fragment::save();
endif;
echo View::factory('profiler/stats');
~~~
## Example: Nested Fragments
You can nest fragments with different lifetimes to provide more specific control. For example, let's say your page has lots of dynamic content so we want to cache it with a lifetime of five minutes, but one of the pieces takes much longer to generate, and only changes every hour anyways. No reason to generate it every 5 minutes, so we will use a nested fragment.
[!!] If a nested fragment has a shorter lifetime than the parent, it will only get processed when the parent has expired.
~~~
// Cache homepage for five minutes
if ( ! Fragment::load('homepage', Date::MINUTE * 5)):
echo "<p>Home page stuff</p>";
// Pretend like we are actually doing something :)
sleep(2);
// Cache this every hour since it doesn't change as often
if ( ! Fragment::load('homepage-subfragment', Date::HOUR)):
echo "<p>Home page special thingy</p>";
// Pretend like this takes a long time
sleep(5);
Fragment::save(); endif;
echo "<p>More home page stuff</p>";
Fragment::save();
endif;
echo View::factory('profiler/stats');
~~~

View File

@@ -0,0 +1,53 @@
# Helpers
Kohana comes with many static "helper" functions to make certain tasks easier.
You can make your own helpers by simply making a class and putting it in the `classes` directory, and you can also extend any helper to modify or add new functions using transparent extension.
- **[Arr]** - Array functions. Get an array key or default to a set value, get an array key by path, etc.
- **[CLI]** - Parse command line options.
- **[Cookie]** - Covered in more detail on the [Cookies](cookies) page.
- **[Date]** - Useful date functions and constants. Time between two dates, convert between am/pm and military, date offset, etc.
- **[Encrypt]** - Covered in more detail on the [Security](security) page.
- **[Feed]** - Parse and create RSS feeds.
- **[File]** - Get file type by mime, split and merge a file into small pieces.
- **[Form]** - Create HTML form elements.
- **[Fragment]** - Simple file based caching. Covered in more detail on the [Fragments](fragments) page.
- **[HTML]** - Useful HTML functions. Encode, obfuscate, create script, anchor, and image tags, etc.
- **[I18n]** - Internationalization helper for creating multilanguage sites.
- **[Inflector]** - Change a word into plural or singular form, camelize or humanize a phrase, etc.
- **[Kohana]** - The Kohana class is also a helper. Debug variables (like print_r but better), file loading, etc.
- **[Num]** - Provides locale aware formating and english ordinals (th, st, nd, etc).
- **[Profiler]** - Covered in more detail on the [Profiling](profiling) page.
- **[Remote]** - Remote server access helper using [CURL](http://php.net/curl).
- **[Request]** - Get the current request url, create expire tags, send a file, get the user agent, etc.
- **[Route]** - Create routes, create an internal link using a route.
- **[Security]** - Covered in more detail on the [Security](security) page.
- **[Session]** - Covered in more detail on the [Sessions](sessions) page.
- **[Text]** - Autolink, prevent window words, convert a number to text, etc.
- **[URL]** - Create a relative or absolute URL, make a URL-safe title, etc.
- **[UTF8]** - Provides multi-byte aware string functions like strlen, strpos, substr, etc.
- **[Upload]** - Helper for uploading files from a form.

View File

@@ -0,0 +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 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.
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.
## 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).
## 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.

View File

@@ -0,0 +1,26 @@
# Installation
1. Download the latest **stable** release from the [Kohana website](http://kohanaframework.org/).
2. Unzip the downloaded package to create a `kohana` directory.
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.
- 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.
6. Make sure the `application/cache` and `application/logs` directories are writable by the web server.
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.
You should see the installation page. If it reports any errors, you will need to correct them before continuing.
![Install Page](install.png "Example of install page")
Once your install page reports that your environment is set up correctly you need to either rename or delete `install.php` in the root directory. Kohana is now installed and you should see the output of the welcome controller:
![Welcome Page](welcome.png "Example of welcome page")
## Installing Kohana 3.1 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.
[!!] For more information on installing Kohana using git submodules, see the [Working with Git](tutorials/git) tutorial.

View File

@@ -0,0 +1,47 @@
## [Kohana]()
- Getting Started
- [Installation](install)
- [Conventions and Style](conventions)
- [Model View Controller](mvc)
- [Controllers](mvc/controllers)
- [Models](mvc/models)
- [Views](mvc/views)
- [Cascading Filesystem](files)
- [Class Files](files/classes)
- [Config Files](files/config)
- [Translation Files](files/i18n)
- [Message Files](files/messages)
- [Request Flow](flow)
- [Bootstrap](bootstrap)
- [Modules](modules)
- [Routing](routing)
- [Error Handling](errors)
- [Tips & Common Mistakes](tips)
- [Upgrading from v3.0](upgrading)
- Basic Usage
- [Debugging](debugging)
- [Loading Classes](autoloading)
- [Transparent Extension](extension)
- [Helpers](helpers)
- [Requests](requests)
- [Sessions](sessions)
- [Cookies](cookies)
- [Fragments](fragments)
- [Profiling](profiling)
- [Security](security)
- [XSS](security/xss)
- [Validation](security/validation)
- [Cookies](security/cookies)
- [Database](security/database)
- [Encryption](security/encryption)
- [Deploying](security/deploying)
- [Tutorials](tutorials)
- [Hello World](tutorials/hello-world)
- [Simple MVC](tutorials/simple-mvc)
- [Custom Error Pages](tutorials/error-pages)
- [Content Translation](tutorials/translation)
- [Clean URLs](tutorials/clean-urls)
- [Sharing Kohana](tutorials/sharing-kohana)
- [Template Driven Site](tutorials/templates)
- [Working with Git](tutorials/git)

View File

@@ -0,0 +1,40 @@
# Modules
Modules are simply an addition to the [Cascading Filesystem](files). A module can add any kind of file (controllers, views, classes, config files, etc.) to the filesystem available to Kohana (via [Kohana::find_file]). This is useful to make any part of your application more transportable or shareable between different apps. For example, creating a new modeling system, a search engine, a css/js manager, etc.
## Where to find modules
Kolanos has created [kohana-universe](http://github.com/kolanos/kohana-universe/tree/master/modules/), a fairly comprehensive list of modules that are available on Github. To get your module listed there, send him a message via Github.
Mon Geslani created a [very nice site](http://kohana.mongeslani.com/) that allows you to sort Github modules by activity, watchers, forks, etc. It seems to not be as comprehensive as kohana-universe.
Andrew Hutchings has created [kohana-modules](http://www.kohana-modules.com) which is similar to the above sites.
## Enabling modules
Modules are enabled by calling [Kohana::modules] and passing an array of `'name' => 'path'`. The name isn't important, but the path obviously is. A module's path does not have to be in `MODPATH`, but usually is. You can only call [Kohana::modules] once.
Kohana::modules(array(
'auth' => MODPATH.'auth', // Basic authentication
'cache' => MODPATH.'cache', // Caching with multiple backends
'codebench' => MODPATH.'codebench', // Benchmarking tool
'database' => MODPATH.'database', // Database access
'image' => MODPATH.'image', // Image manipulation
'orm' => MODPATH.'orm', // Object Relationship Mapping
'oauth' => MODPATH.'oauth', // OAuth authentication
'pagination' => MODPATH.'pagination', // Paging of results
'unittest' => MODPATH.'unittest', // Unit testing
'userguide' => MODPATH.'userguide', // User guide and API documentation
));
## Init.php
When a module is activated, if an `init.php` file exists in that module's directory, it is included. This is the ideal place to have a module include routes or other initialization necessary for the module to function. The Userguide and Codebench modules have init.php files you can look at.
## How modules work
A file in an enabled module is virtually the same as having that exact file in the same place in the application folder. The main difference being that it can be overwritten by a file of the same name in a higher location (a module enabled after it, or the application folder) via the [Cascading Filesystem](files). It also provides an easy way to organize and share your code.
## Creating your own module
To create a module simply create a folder (usually in `DOCROOT/modules`) and place the files you want to be in the module there, and activate that module in your bootstrap. To share your module, you can upload it to [Github](http://github.com). You can look at examples of modules made by [Kohana](http://github.com/kohana) or [other users](#where-to-find-modules).

View File

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

View File

@@ -0,0 +1,182 @@
# Controllers
A Controller is a class file that stands in between the models and the views in an application. It passes information on to the model when data needs to be changed and it requests information from the model when data needs to be loaded. Controllers then pass on the information of the model to the views where the final output can be rendered for the users. Controllers essentially control the flow of the application.
Controllers are called by the [Request::execute()] function based on the [Route] that the url matched. Be sure to read the [routing](routing) page to understand how to use routes to map urls to your controllers.
## Creating Controllers
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`
* 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
class Controller_Foobar extends Controller {
// classes/controller/admin.php
class Controller_Admin extends Controller {
Controllers can be in sub-folders:
// classes/controller/baz/bar.php
class Controller_Baz_Bar extends Controller {
// 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
class Controller_Users extends Controller_Template
// classes/controller/api.php
class Controller_Api extends Controller_REST
[!!] [Controller_Template] and [Controller_REST] are some example controllers 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
class Controller_Admin extends Controller {
// This controller would have a before() that checks if the user is logged in
// 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
## $this->request
Every controller has the `$this->request` property which is the [Request] object that called the controller. You can use this to get information about the current request, as well as set the response body via `$this->response->body($ouput)`.
Here is a partial list of the properties and methods available to `$this->request`. These can also be accessed via `Request::instance()`, but `$this->request` is provided as a shortcut. See the [Request] class for more information on any of these.
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
[$this->response->status()](../api/Response#property:status) | The HTTP status for the request (200, 404, 500, etc.)
[$this->response->headers()](../api/Response#property:headers) | The HTTP headers to return with the response
## Actions
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.
A very basic action method that simply loads a [view](mvc/views) file.
public function action_hello()
{
$this->response->body(View::factory('hello/world')); // This will load views/hello/world.php
}
### Parameters
Parameters are accessed by calling `$this->request->param('name')` where `name` is the name defined in the route.
// Assuming Route::set('example','<controller>(/<action>(/<id>(/<new>)))');
public function action_foobar()
{
$id = $this->request->param('id');
$new = $this->request->param('new');
If that parameter is not set it will be returned as NULL. You can provide a second parameter to set a default value if that param is not set.
public function action_foobar()
{
// $id will be false if it was not supplied in the url
$id = $this->request->param('user',FALSE);
### Examples
A view action for a product page.
public function action_view()
{
$product = new Model_Product($this->request->param('id'));
if ( ! $product->loaded())
{
throw new HTTP_Exception_404('Product not found!');
}
$this->response->body(View::factory('product/view')
->set('product', $product));
}
A user login action.
public function action_login()
{
$view = View::factory('user/login');
if ($_POST)
{
// Try to login
if (Auth::instance()->login(arr::get($_POST, 'username'), arr::get($_POST, 'password')))
{
Request::current()->redirect('home');
}
$view->errors = 'Invalid email or password';
}
$this->response->body($view);
}
## Before and after
You can use the `before()` and `after()` functions to have code executed before or after the action is executed. For example, you could check if the user is logged in, set a template view, loading a required file, etc.
For example, if you look in `Controller_Template` you can see that in the be
You can check what action has been requested (via `$this->request->action`) and do something based on that, such as requiring the user to be logged in to use a controller, unless they are using the login action.
// Checking auth/login in before, and redirecting if necessary:
Controller_Admin extends Controller {
public function before()
{
// 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');
}
}
public function action_login() {
...
### Custom __construct() function
In general, you should not have to change the `__construct()` function, as anything you need for all actions can be done in `before()`. If you need to change the controller constructor, you must preserve the parameters or PHP will complain. This is so the Request object that called the controller is available. *Again, in most cases you should probably be using `before()`, and not changing the constructor*, but if you really, *really* need to it should look like this:
// You should almost never need to do this, use before() instead!
// Be sure Kohana_Request is in the params
public function __construct(Request $request, Response $response)
{
// You must call parent::__construct at some point in your function
parent::__construct($request, $response);
// Do whatever else you want
}
## Extending other controllers
TODO: More description and examples of extending other controllers, multiple extension, etc.

View File

@@ -0,0 +1,35 @@
# Models
From Wikipedia:
> The model manages the behavior and data of the application domain,
> responds to requests for information about its state (usually from the view),
> and responds to instructions to change state (usually from the controller).
Creating a simple model:
class Model_Post extends Model
{
public function do_stuff()
{
// This is where you do domain logic...
}
}
If you want database access, have your model extend the Model_Database class:
class Model_Post extends Model_Database
{
public function do_stuff()
{
// This is where you do domain logic...
}
public function get_stuff()
{
// Get stuff from the database:
return $this->db->query(...);
}
}
If you want CRUD/ORM capabilities, see the [ORM Module](../../guide/orm)

View File

@@ -0,0 +1,153 @@
# Views
Views are files that contain the display information for your application. This is most commonly HTML, CSS and Javascript but can be anything you require such as XML or JSON for AJAX output. The purpose of views is to keep this information separate from your application logic for easy reusability and cleaner code.
Views themselves can contain code used for displaying the data you pass into them. For example, looping through an array of product information and display each one on a new table row. Views are still PHP files so you can use any code you normally would. However, you should try to keep your views as "dumb" as possible and retreive all data you need in your controllers, then pass it to the view.
# Creating View Files
View files are stored in the `views` directory of the [filesystem](files). You can also create sub-directories within the `views` directory to organize your files. All of the following examples are reasonable view files:
APPPATH/views/home.php
APPPATH/views/pages/about.php
APPPATH/views/products/details.php
MODPATH/error/views/errors/404.php
MODPATH/common/views/template.php
## Loading Views
[View] objects will typically be created inside a [Controller](mvc/controllers) using the [View::factory] method. Typically the view is then assigned as the [Request::$response] property or to another view.
public function action_about()
{
$this->response->body(View::factory('pages/about'));
}
When a view is assigned as the [Response::body], as in the example above, it will automatically be rendered when necessary. To get the rendered result of a view you can call the [View::render] method or just type cast it to a string. When a view is rendered, the view file is loaded and HTML is generated.
public function action_index()
{
$view = View::factory('pages/about');
// Render the view
$about_page = $view->render();
// Or just type cast it to a string
$about_page = (string) $view;
$this->response->body($about_page);
}
## Variables in Views
Once view has been loaded, variables can be assigned to it using the [View::set] and [View::bind] methods.
public function action_roadtrip()
{
$view = View::factory('user/roadtrip')
->set('places', array('Rome', 'Paris', 'London', 'New York', 'Tokyo'));
->bind('user', $this->user);
// The view will have $places and $user variables
$this->response->body($view);
}
[!!] The only difference between `set()` and `bind()` is that `bind()` assigns the variable by reference. If you `bind()` a variable before it has been defined, the variable will be created with a value of `NULL`.
You can also assign variables directly to the View object. This is identical to calling `set()`;
public function action_roadtrip()
{
$view = View::factory('user/roadtrip');
$view->places = array('Rome', 'Paris', 'London', 'New York', 'Tokyo');
$view->user = $this->user;
// The view will have $places and $user variables
$this->response->body($view);
}
### Global Variables
An application may have several view files that need access to the same variables. For example, to display a page title in both the header of your template and in the body of the page content. You can create variables that are accessible in any view using the [View::set_global] and [View::bind_global] methods.
// Assign $page_title to all views
View::bind_global('page_title', $page_title);
If the application has three views that are rendered for the home page: `template`, `template/sidebar`, and `pages/home`. First, an abstract controller to create the template will be created:
abstract class Controller_Website extends Controller_Template {
public $page_title;
public function before()
{
parent::before();
// Make $page_title available to all views
View::bind_global('page_title', $this->page_title);
// Load $sidebar into the template as a view
$this->template->sidebar = View::factory('template/sidebar');
}
}
Next, the home controller will extend `Controller_Website`:
class Controller_Home extends Controller_Website {
public function action_index()
{
$this->page_title = 'Home';
$this->template->content = View::factory('pages/home');
}
}
## Views Within Views
If you want to include another view within a view, there are two choices. By calling [View::factory] you can sandbox the included view. This means that you will have to provide all of the variables to the view using [View::set] or [View::bind]:
// In your view file:
// Only the $user variable will be available in "views/user/login.php"
<?php echo View::factory('user/login')->bind('user', $user) ?>
The other option is to include the view directly, which makes all of the current variables available to the included view:
// In your view file:
// Any variable defined in this view will be included in "views/message.php"
<?php include Kohana::find_file('views', 'user/login') ?>
You can also assign a variable of your parent view to be the child view from within your controller. For example:
// In your controller:
public functin action_index()
{
$view = View::factory('common/template);
$view->title = "Some title";
$view->body = View::factory('pages/foobar');
}
// In views/common/template.php:
<html>
<head>
<title><?php echo $title></title>
</head>
<body>
<?php echo $body ?>
</body>
</html>
Of course, you can also load an entire [Request] within a view:
<?php echo Request::factory('user/login')->execute() ?>
This is an example of \[HMVC], which makes it possible to create and read calls to other URLs within your application.

View File

@@ -0,0 +1,54 @@
# Profiling
Kohana provides a very simple way to display statistics about your application:
1. Common [Kohana] method calls, such as [Kohana::find_file()].
2. Requests. Including the main request, as well as any sub-requests.
3. [Database] queries
4. Average execution times for your application
[!!] In order for profiling to work, the `profile` setting must be `TRUE` in your [Kohana::init()] call in your bootstrap.
## Profiling your code
You can easily add profiling to your own functions and code. This is done using the [Profiler::start()] function. The first parameter is the group, the second parameter is the name of the benchmark.
public function foobar($input)
{
// Be sure to only profile if it's enabled
if (Kohana::$profiling === TRUE)
{
// Start a new benchmark
$benchmark = Profiler::start('Your Category', __FUNCTION__);
}
// Do some stuff
if (isset($benchmark))
{
// Stop the benchmark
Profiler::stop($benchmark);
}
return $something;
}
## How to read the profiling report
The benchmarks are sorted into groups. Each benchmark will show its name, how many times it was run (show in parenthesis after the benchmark name), and then the min, max, average, and total time and memory spent on that benchmark. The total column will have shaded backgrounds to show the relative times between benchmarks in the same group.
At the very end is a group called "Application Execution". This keeps track of how long each execution has taken. The number in parenthesis is how many executions are being compared. It shows the fastest, slowest, and average time and memory usage of the last several requsets. The last box is the time and memory usage of the current request.
((This could use a picture of a profiler with some database queries, etc. with annotations to point out each area as just described.))
## Displaying the profiler
You can display or collect the current [profiler] statistics at any time:
<?php echo View::factory('profiler/stats') ?>
## Preview
(This is the actual profiler stats for this page.)
{{profiler/stats}}

View File

@@ -0,0 +1,65 @@
# Requests
Kohana includes a flexible HMVC request system. It supports out of the box support for internal requests and external requests.
HMVC stands for `Hierarchical Model View Controller` and basically means requests can each have MVC triads called from inside each other.
The Request object in Kohana is HTTP/1.1 compliant.
## Creating Requests
Creating a request is very easy:
### Internal Requests
An internal request is a request calling to the internal application. It utilizes [routes](routing) to direct the application based on the URI that is passed to it. A basic internal request might look something like:
$request = Request::factory('welcome');
In this example, the URI is 'welcome'.
#### The initial request
Since Kohana uses HMVC, you can call many requests inside each other. The first request (usually called from `index.php`) is called the "initial request". You can access this request via:
Request::initial();
You should only use this method if you are absolutely sure you want the initial request. Otherwise you should use the `Request::current()` method.
#### Sub-requests
You can call a request at any time in your application by using the `Request::factory()` syntax. All of these requests will be considered sub-requests.
Other than this difference, they are exactly the same. You can detect if the request is a sub-request in your controller with the is_initial() method:
$sub_request = ! $this->request->is_initial()
### External Requests
An external request calls out to a third party website.
You can use this to scrape HTML from a remote site, or make a REST call to a third party API:
// This uses GET
$request = Request::factory('http://www.google.com/');
// This uses PUT
$request = Request::factory('http://example.com/put_api')->method(Request::PUT)->body(json_encode('the body'))->headers('Content-Type', 'application/json');
// This uses POST
$request = Request::factory('http://example.com/post_api')->method(Request::POST)->post(array('foo' => 'bar', 'bar' => 'baz'));
## Executing Requests
To execute a request, use the `execute()` method on it. This will give you a [response](responses) object.
$request = Request::factory('welcome');
$response = $request->execute();
## 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());
TODO

View File

@@ -0,0 +1,271 @@
# Routing
Kohana provides a very powerful routing system. In essence, routes provide an interface between the urls and your controllers and actions. With the correct routes you could make almost any url scheme correspond to almost any arrangement of controllers, and you could change one without impacting the other.
As mentioned in the [Request Flow](flow) section, a request is handled by the [Request] class, which will look for a matching [Route] and load the appropriate controller to handle that request.
[!!] It is important to understand that **routes are matched in the order they are added**, and as soon as a URL matches a route, routing is essentially "stopped" and *the remaining routes are never tried*. Because the default route matches almost anything, including an empty url, new routes must be place before it.
## Creating routes
If you look in `APPPATH/bootstrap.php` you will see the "default" route as follows:
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
[!!] The default route is simply provided as a sample, you can remove it and replace it with your own routes.
So this creates a route with the name `default` that will match urls in the format of `(<controller>(/<action>(/<id>)))`.
Let's take a closer look at each of the parameters of [Route::set], which are `name`, `uri`, and an optional array `regex`.
### Name
The name of the route must be a **unique** string. If it is not it will overwrite the older route with the same name. The name is used for creating urls by reverse routing, or checking which route was matched.
### URI
The uri is a string that represents the format of urls that should be matched. The tokens surrounded with `<>` are *keys* and anything surrounded with `()` are *optional* parts of the uri. In Kohana routes, any character is allowed and treated literally aside from `()<>`. The `/` has no meaning besides being a character that must match in the uri. Usually the `/` is used as a static seperator but as long as the regex makes sense, there are no restrictions to how you can format your routes.
Lets look at the default route again, the uri is `(<controller>(/<action>(/<id>)))`. We have three keys or params: controller, action, and id. In this case, the entire uri is optional, so a blank uri would match and the default controller and action (set by defaults(), [covered below](#defaults)) would be assumed resulting in the `Controller_Welcome` class being loaded and the `action_index` method being called to handle the request.
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))
* **Controller** - The controller that the request should execute.
* **Action** - The action method to call.
### Regex
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`.
Route::set('sections', '<directory>(/<controller>(/<action>(/<id>)))',
array(
'directory' => '(admin|affiliate)'
))
->defaults(array(
'controller' => 'home',
'action' => 'index',
));
You can also use a less restrictive regex to match unlimited parameters, or to ignore overflow in a route. In this example, the url `foobar/baz/and-anything/else_that/is-on-the/url` would be routed to `Controller_Foobar::action_baz()` and the `"stuff"` parameter would be `"and-anything/else_that/is-on-the/url"`. If you wanted to use this for unlimited parameters, you could [explode](http://php.net/manual/en/function.explode.php) it, or you just ignore the overflow.
Route::set('default', '(<controller>(/<action>(/<stuff>)))', array('stuff' => '.*'))
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
### Default values
If a key in a route is optional (or not present in the route), you can provide a default value for that key by passing an associated array of keys and default values to [Route::defaults], chained after your [Route::set]. This can be useful to provide a default controller or action for your site, among other things.
[!!] 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.
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
You can also use defaults to set a key that isn't in the route at all.
TODO: example of either using directory or controller where it isn't in the route, but set by defaults
### Directory
## Lambda/Callback route logic
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:
If you want to use reverse routing with lambda routes, you must pass the third parameter:
Route::set('testing', function($uri)
{
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/>(.+)')
{
Cookie::set('language', $match[1]);
return array(
'controller' => 'welcome',
'action' => 'foobar'
);
}
},
'<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):
Route::set('testing', array('Class', 'method_to_process_my_uri'));
## Examples
There are countless other possibilities for routes. Here are some more examples:
/*
* Authentication shortcuts
*/
Route::set('auth', '<action>',
array(
'action' => '(login|logout)'
))
->defaults(array(
'controller' => 'auth'
));
/*
* Multi-format feeds
* 452346/comments.rss
* 5373.json
*/
Route::set('feeds', '<user_id>(/<action>).<format>',
array(
'user_id' => '\d+',
'format' => '(rss|atom|json)',
))
->defaults(array(
'controller' => 'feeds',
'action' => 'status',
));
/*
* Static pages
*/
Route::set('static', '<path>.html',
array(
'path' => '[a-zA-Z0-9_/]+',
))
->defaults(array(
'controller' => 'static',
'action' => 'index',
));
/*
* You don't like slashes?
* EditGallery:bahamas
* Watch:wakeboarding
*/
Route::set('gallery', '<action>(<controller>):<id>',
array(
'controller' => '[A-Z][a-z]++',
'action' => '[A-Z][a-z]++',
))
->defaults(array(
'controller' => 'Slideshow',
));
/*
* Quick search
*/
Route::set('search', ':<query>', array('query' => '.*'))
->defaults(array(
'controller' => 'search',
'action' => 'index',
));
## Request parameters
The `directory`, `controller` and `action` can be accessed from the [Request] as public properties like so:
// From within a controller:
$this->request->action();
$this->request->controller();
$this->request->directory();
// Can be used anywhere:
Request::current()->action();
Request::current()->controller();
Request::current()->directory();
All other keys specified in a route can be accessed via [Request::param()]:
// From within a controller:
$this->request->param('key_name');
// Can be used anywhere:
Request::current()->param('key_name');
The [Request::param] method takes an optional second argument to specify a default return value in case the key is not set by the route. If no arguments are given, all keys are returned as an associative array. In addition, `action`, `controller` and `directory` are not accessible via [Request::param()].
For example, with the following route:
Route::set('ads','ad/<ad>(/<affiliate>)')
->defaults(array(
'controller' => 'ads',
'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()`.
class Controller_Ads extends Controller {
public function action_index()
{
$ad = $this->request->param('ad');
$affiliate = $this->request->param('affiliate',NULL);
}
## Where should routes be defined?
The established convention is to either place your custom routes in the `MODPATH/<module>/init.php` file of your module if the routes belong to a module, or simply insert them into the `APPPATH/bootstrap.php` file (be sure to put them **above** the default route) if they are specific to the application. Of course, nothing stops you from including them from an external file, or even generating them dynamically.
## A deeper look at how routes work
TODO: talk about how routes are compiled
## Creating URLs and links using routes
Along with Kohana's powerful routing capabilities are included some methods for generating URLs for your routes' uris. You can always specify your uris as a string using [URL::site] to create a full URL like so:
URL::site('admin/edit/user/'.$user_id);
However, Kohana also provides a method to generate the uri from the route's definition. This is extremely useful if your routing could ever change since it would relieve you from having to go back through your code and change everywhere that you specified a uri as a string. Here is an example of dynamic generation that corresponds to the `feeds` route example from above:
Route::get('feeds')->uri(array(
'user_id' => $user_id,
'action' => 'comments',
'format' => 'rss'
));
Let's say you decided later to make that route definition more verbose by changing it to `feeds/<user_id>(/<action>).<format>`. If you wrote your code with the above uri generation method you wouldn't have to change a single line! When a part of the uri is enclosed in parentheses and specifies a key for which there in no value provided for uri generation and no default value specified in the route, then that part will be removed from the uri. An example of this is the `(/<id>)` part of the default route; this will not be included in the generated uri if an id is not provided.
One method you might use frequently is the shortcut [Request::uri] which is the same as the above except it assumes the current route, directory, controller and action. If our current route is the default and the uri was `users/list`, we can do the following to generate uris in the format `users/view/$id`:
$this->request->uri(array('action' => 'view', 'id' => $user_id));
Or if within a view, the preferable method is:
Request::instance()->uri(array('action' => 'view', 'id' => $user_id));
TODO: examples of using html::anchor in addition to the above examples
## Testing routes
TODO: mention bluehawk's devtools module

View File

@@ -0,0 +1 @@
General security concerns, like using the Security class, CSRF, and a brief intro to XSS, database security, etc. Also mention the security features that Kohana provides, like cleaning globals.

View File

@@ -0,0 +1,3 @@
Discuss security of cookies, like changing the encryption key in the config.
Not sure why I'm linking to this: <http://kohanaframework.org/guide/security.cookies>

View File

@@ -0,0 +1,5 @@
Discuss database security.
How to avoid injection, etc.
Not sure why I'm linking to this: <http://kohanaframework.org/guide/security.database>

View File

@@ -0,0 +1,61 @@
Changes that should happen when you deploy. (Production)
Security settings from: <http://kohanaframework.org/guide/using.configuration>
<http://kerkness.ca/wiki/doku.php?id=setting_up_production_environment>
## Setting up a production environment
There are a few things you'll want to do with your application before moving into production.
1. See the [Bootstrap page](bootstrap) in the docs.
This covers most of the global settings that would change between environments.
As a general rule, you should enable caching and disable profiling ([Kohana::init] settings) for production sites.
[Route::cache] can also help if you have a lot of routes.
2. Turn on APC or some kind of opcode caching.
This is the single easiest performance boost you can make to PHP itself. The more complex your application, the bigger the benefit of using opcode caching.
/**
* Set the environment string by the domain (defaults to Kohana::DEVELOPMENT).
*/
Kohana::$environment = ($_SERVER['SERVER_NAME'] !== 'localhost') ? Kohana::PRODUCTION : Kohana::DEVELOPMENT;
/**
* Initialise Kohana based on environment
*/
Kohana::init(array(
'base_url' => '/',
'index_file' => FALSE,
'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

@@ -0,0 +1 @@
Discuss using encryption, including setting the encryption key in config.

View File

@@ -0,0 +1,234 @@
# Validation
*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".
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
[!!] Note that any valid [PHP callback](http://php.net/manual/language.pseudo-types.php#language.types.callback) can be used as a rule.
Using `TRUE` as the field name when adding a rule will be applied to all named fields.
Creating a validation object is done using the [Validation::factory] method:
$post = Validation::factory($_POST);
[!!] 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.
## Provided Rules
Kohana provides a set of useful rules in the [Valid] class:
Rule name | Function
------------------------- |-------------------------------------------------
[Valid::not_empty] | Value must be a non-empty value
[Valid::regex] | Match the value against a regular expression
[Valid::min_length] | Minimum number of characters for value
[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::url] | Value must be a URL
[Valid::ip] | Value must be an IP address
[Valid::phone] | Value must be a phone number
[Valid::credit_card] | Require a credit card number
[Valid::date] | Value must be a date (and time)
[Valid::alpha] | Only alpha characters allowed
[Valid::alpha_dash] | Only alpha and hyphens allowed
[Valid::alpha_numeric] | Only alpha and numbers allowed
[Valid::digit] | Value must be an integer digit
[Valid::decimal] | Value must be a decimal or float value
[Valid::numeric] | Only numeric characters allowed
[Valid::range] | Value must be within a range
[Valid::color] | Value must be a valid HEX color
[Valid::matches] | Value matches another field value
## 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:
$object->rule($field, $callback, array($parameter1, $parameter2));
If no parameters are specified, the field value will be passed to the callback. The following two rules are equivalent:
$object->rule($field, 'not_empty');
$object->rule($field, 'not_empty', array(':value'));
Rules defined in the [Valid] class can be added by using the method name alone. The following three rules are equivalent:
$object->rule('number', 'phone');
$object->rule('number', array('Valid', 'phone'));
$object->rule('number', 'Valid::phone');
## 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.
$object->bind(':model', $user_model);
// Future code will be able to use :model to reference the object
$object->rule('username', 'some_rule', array(':model'));
By default, the validation object will automatically bind the following values for you to use as rule parameters:
- `:validation` - references the validation object
- `:field` - references the field name the rule is for
- `:value` - references the value of the field the rule is for
## Adding Errors
The [Validation] class will add an error for a field if any of the rules associated to it return `FALSE`. This allows many built in PHP functions to be used as rules, like `in_array`.
$object->rule('color', 'in_array', array(':value', array('red', 'green', 'blue')));
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)
{
$validation->error($field, 'the_rule');
}
}
[!!] `not_empty` and `matches` are the only rules that will run on empty fields and add errors by returning `FALSE`.
## Example
To start our example, we will perform validation on a `$_POST` array that contains user registration information:
$post = Validation::factory($_POST);
Next we need to process the POST'ed information using [Validation]. To start, we need to add some rules:
$post
->rule('username', 'not_empty')
->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD'))
->rule('password', 'not_empty')
->rule('password', 'min_length', array(':value', '6'))
->rule('confirm', 'matches', array(':validation', 'confirm', 'password'))
->rule('use_ssl', 'not_empty');
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')));
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');
The method `User_Model::unique_username()` would be defined similar to:
public static function unique_username($username)
{
// Check if the username already exists in the database
return ! DB::select(array(DB::expr('COUNT(username)'), 'total'))
->from('users')
->where('username', '=', $username)
->execute()
->get('total');
}
[!!] Custom rules allow many additional checks to be reused for multiple purposes. These methods will almost always exist in a model, but may be defined in any class.
# A Complete Example
First, we need a [View] that contains the HTML form, which will be placed in `application/views/user/register.php`:
<?php echo Form::open() ?>
<?php if ($errors): ?>
<p class="message">Some errors were encountered, please check the details you entered.</p>
<ul class="errors">
<?php foreach ($errors as $message): ?>
<li><?php echo $message ?></li>
<?php endforeach ?>
<?php endif ?>
<dl>
<dt><?php echo Form::label('username', 'Username') ?></dt>
<dd><?php echo Form::input('username', $post['username']) ?></dd>
<dt><?php echo Form::label('password', 'Password') ?></dt>
<dd><?php echo Form::password('password') ?></dd>
<dd class="help">Passwords must be at least 6 characters long.</dd>
<dt><?php echo Form::label('confirm', 'Confirm Password') ?></dt>
<dd><?php echo Form::password('confirm') ?></dd>
<dt><?php echo Form::label('use_ssl', 'Use extra security?') ?></dt>
<dd><?php echo Form::select('use_ssl', array('yes' => 'Always', 'no' => 'Only when necessary'), $post['use_ssl']) ?></dd>
<dd class="help">For security, SSL is always used when making payments.</dd>
</dl>
<?php echo Form::submit(NULL, 'Sign Up') ?>
<?php echo Form::close() ?>
[!!] 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`:
class Controller_User extends Controller {
public function action_register()
{
$user = Model::factory('user');
$post = Validate::factory($_POST)
->rule('username', 'not_empty')
->rule('username', 'regex', array(':value', '/^[a-z_.]++$/iD'))
->rule('username', array($user, 'unique_username'))
->rule('password', 'not_empty')
->rule('password', 'min_length', array(':value', 6))
->rule('confirm', 'matches', array(':validation', ':field', 'password'))
->rule('use_ssl', 'not_empty')
->rule('use_ssl', 'in_array', array(':value', array('yes', 'no')));
if ($post->check())
{
// Data has been validated, register the user
$user->register($post);
// Always redirect after a successful POST to prevent refresh warnings
$this->request->redirect('user/profile');
}
// Validation failed, collect the errors
$errors = $post->errors('user');
// Display the registration form
$this->response->body(View::factory('user/register'))
->bind('post', $post)
->bind('errors', $errors);
}
}
We will also need a user model, which will be placed in `application/classes/model/user.php`:
class Model_User extends Model {
public function register($array)
{
// Create a new user record in the database
$id = DB::insert(array_keys($array))
->values($array)
->execute();
// Save the new user id to a cookie
cookie::set('user', $id);
return $id;
}
}
That is it, we have a complete user registration example that properly checks user input!

View File

@@ -0,0 +1,17 @@
# Cross-Site Scripting (XSS) Security
*This page is not comprehensive and should not be considered a complete guide to XSS prevention.*
The first step to preventing [XSS](http://wikipedia.org/wiki/Cross-Site_Scripting) attacks is knowing when you need to protect yourself. XSS can only be triggered when it is displayed within HTML content, sometimes via a form input or being displayed from database results. Any global variable that contains client information can be tainted. This includes `$_GET`, `$_POST`, and `$_COOKIE` data.
## Prevention
There are a few simple rules to follow to guard your application HTML against XSS. If you do not want HTML in a variable, use [strip_tags](http://php.net/strip_tags) to remove all unwanted HTML tags from a value.
[!!] If you allow users to submit HTML to your application, it is highly recommended to use an HTML cleaning tool such as [HTML Purifier](http://htmlpurifier.org/) or [HTML Tidy](http://php.net/tidy).
The second is to always escape data when inserting into HTML. The [HTML] class provides generators for many common tags, including script and stylesheet links, anchors, images, and email (mailto) links. Any untrusted content should be escaped using [HTML::chars].
## References
* [OWASP XSS Cheat Sheet](http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)

View File

@@ -0,0 +1,167 @@
# Sessions
Kohana provides classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same functionality. They allow the developer to store temporary or persistent information about a specific client for later retrieval, usually to make something persistent between requests.
Sessions should be used for storing temporary or private data. Very sensitive data should be stored using the [Session] class with the "database" or "native" adapters. When using the "cookie" adapter, the session should always be encrypted.
[!!] For more information on best practices with session variables see [the seven deadly sins of sessions](http://lists.nyphp.org/pipermail/talk/2006-December/020358.html).
## Storing, Retrieving, and Deleting Data
[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class.
Accessing the session instance is done using the [Session::instance] method:
// Get the session instance
$session = Session::instance();
When using sessions, you can also get all of the current session data using the [Session::as_array] method:
// Get all of the session data as an array
$data = $session->as_array();
You can also use this to overload the `$_SESSION` global to get and set data in a way more similar to standard PHP:
// Overload $_SESSION with the session data
$_SESSION =& $session->as_array();
// Set session data
$_SESSION[$key] = $value;
### Storing Data
Storing session or cookie data is done using the `set` method:
// Set session data
$session->set($key, $value);
// Or
Session::instance()->set($key, $value);
// Store a user id
$session->set('user_id', 10);
### Retrieving Data
Getting session or cookie data is done using the `get` method:
// Get session data
$data = $session->get($key, $default_value);
// Get the user id
$user = $session->get('user_id');
### Deleting Data
Deleting session or cookie data is done using the `delete` method:
// Delete session data
$session->delete($key);
// Delete the user id
$session->delete('user_id');
## Session Configuration
Always check these settings before making your application live, as many of them will have a direct affect on the security of your application.
## Session Adapters
When creating or accessing an instance of the [Session] class you can decide which session adapter or driver you wish to use. The session adapters that are available to you are:
Native
: Stores session data in the default location for your web server. The storage location is defined by [session.save_path](http://php.net/manual/session.configuration.php#ini.session.save-path) in `php.ini` or defined by [ini_set](http://php.net/ini_set).
Database
: Stores session data in a database table using the [Session_Database] class. Requires the [Database] module to be enabled.
Cookie
: Stores session data in a cookie using the [Cookie] class. **Sessions will have a 4KB limit when using this adapter, and should be encrypted.**
The default adapter can be set by changing the value of [Session::$default]. The default adapter is "native".
To access a Session using the default adapter, simply call [Session::instance()]. To access a Session using something other than the default, pass the adapter name to `instance()`, for example: `Session::instance('cookie')`
### Session Adapter Settings
You can apply configuration settings to each of the session adapters by creating a session config file at `APPPATH/config/session.php`. The following sample configuration file defines all the settings for each adapter:
[!!] As with cookies, a "lifetime" setting of "0" means that the session will expire when the browser is closed.
return array(
'native' => array(
'name' => 'session_name',
'lifetime' => 43200,
),
'cookie' => array(
'name' => 'cookie_name',
'encrypted' => TRUE,
'lifetime' => 43200,
),
'database' => array(
'name' => 'cookie_name',
'encrypted' => TRUE,
'lifetime' => 43200,
'group' => 'default',
'table' => 'table_name',
'columns' => array(
'session_id' => 'session_id',
'last_active' => 'last_active',
'contents' => 'contents'
),
'gc' => 500,
),
);
#### Native Adapter
Type | Setting | Description | Default
----------|-----------|---------------------------------------------------|-----------
`string` | name | name of the session | `"session"`
`integer` | lifetime | number of seconds the session should live for | `0`
#### Cookie Adapter
Type | Setting | Description | Default
----------|-----------|---------------------------------------------------|-----------
`string` | name | name of the cookie used to store the session data | `"session"`
`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE`
`integer` | lifetime | number of seconds the session should live for | `0`
#### Database Adapter
Type | Setting | Description | Default
----------|-----------|---------------------------------------------------|-----------
`string` | group | [Database::instance] group name | `"default"`
`string` | table | table name to store sessions in | `"sessions"`
`array` | columns | associative array of column aliases | `array`
`integer` | gc | 1:x chance that garbage collection will be run | `500`
`string` | name | name of the cookie used to store the session data | `"session"`
`boolean` | encrypted | encrypt the session data using [Encrypt]? | `FALSE`
`integer` | lifetime | number of seconds the session should live for | `0`
##### Table Schema
You will need to create the session storage table in the database. This is the default schema:
CREATE TABLE `sessions` (
`session_id` VARCHAR(24) NOT NULL,
`last_active` INT UNSIGNED NOT NULL,
`contents` TEXT NOT NULL,
PRIMARY KEY (`session_id`),
INDEX (`last_active`)
) ENGINE = MYISAM;
##### Table Columns
You can change the column names to match an existing database schema when connecting to a legacy session table. The default value is the same as the key value.
session_id
: the name of the "id" column
last_active
: UNIX timestamp of the last time the session was updated
contents
: session data stored as a serialized string, and optionally encrypted

View File

@@ -0,0 +1,33 @@
# Tips and Common Mistakes
This is a collection of tips and common mistakes or errors you may encounter.
## Never edit the `system` folder!
You should (almost) never edit the system folder. Any change you want to make to files in system and modules can be made via the [cascading filesystem](files) and [transparent extension](extension) and won't break when you try to update your Kohana version.
## Don't try and use one route for everything
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!
## 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).
ReflectionException [ -1 ]: Class controller_<something> does not exist
// where <something> is part of the url you entered in your browser
### Solution {#reflection-exception-solution}
Set your [Kohana::init] 'base_url' to the correct setting. The base url should be the path to your index.php file relative to the webserver document root.
## ORM/Session __sleep() bug
There is a bug in php which can corrupt your session after a fatal error. A production server shouldn't have uncaught fatal errors, so this bug should only happen during development, when you do something stupid and cause a fatal error. On the next page load you will get a database connection error, then all subsequent page loads will display the following error:
ErrorException [ Notice ]: Undefined index: id
MODPATH/orm/classes/kohana/orm.php [ 1308 ]
### Solution {#orm-session-sleep-solution}
To fix this, clear your cookies for that domain to reset your session. This should never happen on a production server, so you won't have to explain to your clients how to clear their cookies. You can see the [discussion on this issue](http://dev.kohanaframework.org/issues/3242) for more details.

View File

@@ -0,0 +1,17 @@
# Tutorials
## Tutorials in this guide
## Tutorials written elsewhere
### Ellisgl's KO3 tutorial on dealtaker.com:
1. [Install and Basic Usage](http://www.dealtaker.com/blog/2009/11/20/kohana-php-3-0-ko3-tutorial-part-1/)
2. [Views](http://www.dealtaker.com/blog/2009/12/07/kohana-php-3-0-ko3-tutorial-part-2/)
3. [Controllers](http://www.dealtaker.com/blog/2009/12/30/kohana-php-3-0-ko3-tutorial-part-3/)
4. [Models](http://www.dealtaker.com/blog/2010/02/01/kohana-php-3-0-ko3-tutorial-part-4/)
5. [Subrequests](http://www.dealtaker.com/blog/2010/02/25/kohana-php-3-0-ko3-tutorial-part-5/)
6. [Routes](http://www.dealtaker.com/blog/2010/03/03/kohana-php-3-0-ko3-tutorial-part-6/)
7. [Helpers](http://www.dealtaker.com/blog/2010/03/26/kohana-php-3-0-ko3-tutorial-part-7/)
8. [Modules](http://www.dealtaker.com/blog/2010/04/30/kohana-php-3-0-ko3-tutorial-part-8/)
9. [Vendor Libraries](http://www.dealtaker.com/blog/2010/06/02/kohana-php-3-0-ko3-tutorial-part-9/)

View File

@@ -0,0 +1,80 @@
# Clean URLs
Removing `index.php` from your urls.
To keep your URLs clean, you will probably want to be able to access your app without having `/index.php/` in the URL. There are two steps to remove `index.php` from the URL.
1. Edit the bootstrap file
2. Set up rewriting
## 1. Configure Bootstrap
The first thing you will need to change is the `index_file` setting of [Kohana::init] to false:
Kohana::init(array(
'base_url' => '/myapp/',
'index_file' => FALSE,
));
This change will make it so all of the links generated using [URL::site], [URL::base], and [HTML::anchor] will no longer include "index.php" in the URL. All generated links will start with `/myapp/` instead of `/myapp/index.php/`.
## 2. URL Rewriting
Enabling rewriting is done differently, depending on your web server.
Rewriting will make it so urls will be passed to index.php.
## Apache
Rename `example.htaccess` to only `.htaccess` and alter the `RewriteBase` line to match the `base_url` setting from your [Kohana::init]
RewriteBase /myapp/
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!
### Failed!
If you get a "Internal Server Error" or "No input file specified" error, try changing:
RewriteRule ^(?:application|modules|system)\b - [F,L]
Instead, we can try a slash:
RewriteRule ^(application|modules|system)/ - [F,L]
If that doesn't work, try changing:
RewriteRule .* index.php/$0 [PT]
To something more simple:
RewriteRule .* index.php [PT]
### 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`:
<Directory "/var/www/html/myapp">
Order allow,deny
Allow from all
AllowOverride All
</Directory>
You should also check your Apache logs to see if they can shed some light on the error.
## NGINX
It is hard to give examples of nginx configuration, but here is a sample for a server:
location / {
index index.php index.html index.htm;
try_files $uri index.php;
}
location = index.php {
include fastcgi.conf;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
}
If you are having issues getting this working, enable debug level logging in nginx and check the access and error logs.

View File

@@ -0,0 +1,154 @@
# Friendly 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.
## Prerequisites
You will need `'errors' => TRUE` passed to `Kohana::init`. This will convert PHP
errors into exceptions which are easier to handle.
## 1. An Improved Exception Handler
Our custom exception handler is self explanatory.
class Kohana_Exception extends Kohana_Kohana_Exception {
public static function handler(Exception $e)
{
if (Kohana::DEVELOPMENT === Kohana::$environment)
{
parent::handler($e);
}
else
{
try
{
Kohana::$log->add(Log::ERROR, parent::text($e));
$attributes = array
(
'action' => 500,
'message' => rawurlencode($e->getMessage())
);
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);
}
}
}
}
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

@@ -0,0 +1,143 @@
# Creating a New Application
[!!] The following examples assume that your web server is already set up, and you are going to create a new application at <http://localhost/gitorial/>.
Using your console, change to the empty directory `gitorial` and run `git init`. This will create the bare structure for a new git repository.
Next, we will create a [submodule](http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html) for the `system` directory. Go to <http://github.com/kohana/core> and copy the "Clone URL":
![Github Clone URL](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)
Now use the URL to create the submodule for `system`:
git submodule add git://github.com/kohana/core.git system
[!!] This will create a link to the current development version of the next stable release. The development version should almost always be safe to use, have the same API as the current stable download with bugfixes applied.
Now add whatever submodules you need. For example, if you need the [Database] module:
git submodule add git://github.com/kohana/database.git modules/database
After submodules are added, they must be initialized:
git submodule init
Now that the submodules are added, you can commit them:
git commit -m 'Added initial submodules'
Next, create the application directory structure. This is the bare minimum required:
mkdir -p application/classes/{controller,model}
mkdir -p application/{config,views}
mkdir -m 0777 -p application/{cache,logs}
If you run `find application` you should see this:
application
application/cache
application/config
application/classes
application/classes/controller
application/classes/model
application/logs
application/views
We don't want git to track log or cache files, so add a `.gitignore` file to each of the directories. This will ignore all non-hidden files:
echo '[^.]*' > application/{logs,cache}/.gitignore
[!!] Git ignores empty directories, so adding a `.gitignore` file also makes sure that git will track the directory, but not the files within it.
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
Commit these changes too:
git add application
git commit -m 'Added initial directory structure'
That's all there is to it. You now have an application that is using Git for versioning.
## Adding Submodules
To add a new submodule complete the following steps:
1. run the following code - git submodule add repository path for each new submodule e.g.:
git submodule add git://github.com/shadowhand/sprig.git modules/sprig
2. then init and update the submodules:
git submodule init
git submodule update
## Updating Submodules
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'
To update a single submodule, for example, `system`:
cd system
git checkout 3.1/master
git pull origin 3.1/master
cd ..
git add system
git commit -m 'Updated system to latest version'
If you want to update a single submodule to a specific commit:
cd modules/database
git pull origin 3.1/master
git checkout fbfdea919028b951c23c3d99d2bc1f5bbeda0c0b
cd ../..
git add database
git commit -m 'Updated database module'
Note that you can also check out the commit at a tagged official release point, for example:
git checkout 3.1.0
Simply run `git tag` without arguments to get a list of all tags.
## Removing Submodules
To remove a submodule that is no longer needed complete the following steps:
1. open .gitmodules and remove the reference to the to submodule
It will look something like this:
[submodule "modules/auth"]
path = modules/auth
url = git://github.com/kohana/auth.git
2. open .git/config and remove the reference to the to submodule\\
[submodule "modules/auth"]
url = git://github.com/kohana/auth.git
3. run git rm --cached path/to/submodule, e.g.
git rm --cached modules/auth
**Note:** Do not put a trailing slash at the end of path. If you put a trailing slash at the end of the command, it will fail.
## Updating Remote Repository URL
During the development of a project, the source of a submodule may change for any reason (you've created your own fork, the server URL changed, the repository name or path changed, etc...) and you'll have to update those changes. To do so, you'll need to perform the following steps:
1. edit the .gitmodules file, and change the URL for the submodules which changed.
2. in your source tree's root run:
git submodule sync
3. run `git init` to update the project's repository configuration with the new URLs:
git submodule init
And it's done, now you can continue pushing and pulling your submodules with no problems.
Source: http://jtrancas.wordpress.com/2011/02/06/git-submodule-location/

View File

@@ -0,0 +1,106 @@
# Hello, World
Just about every framework ever written has some kind of hello world example included, so it'd be pretty rude of us to break this tradition!
We'll start out by creating a very very basic hello world, and then we'll expand it to follow MVC principles.
## Bare bones
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:
<?php defined('SYSPATH') OR die('No Direct Script Access');
Class Controller_Hello extends Controller
{
public function action_index()
{
echo 'hello, world!';
}
}
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.
`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.
`public function action_index()`
: This defines the "index" action of our controller. Kohana will attempt to call this action if the user hasn't specified an action. (See [Routes, URLs and Links](tutorials.urls))
`echo 'hello, world!';`
: And this is the line which outputs the customary phrase!
Now if you open your browser and go to http://localhost/index.php/hello you should see something like:
![Hello, World!](hello_world_1.png "Hello, World!")
## That was good, but we can do better
What we did in the previous section was a good example of how easy it to create an *extremely* basic Kohana app. (In fact it's so basic, that you should never make it again!)
If you've ever heard anything about MVC you'll probably have realised that echoing content out in a controller is strictly against the principles of MVC.
The proper way to code with an MVC framework is to use _views_ to handle the presentation of your application, and allow the controller to do what it does best control the flow of the request!
Lets change our original controller slightly:
<?php defined('SYSPATH') OR die('No Direct Script Access');
Class Controller_Hello extends Controller_Template
{
public $template = 'site';
public function action_index()
{
$this->template->message = 'hello, world!';
}
}
`extends Controller_Template`
: We're now extending the template controller, it makes it more convenient to use views within our controller.
`public $template = 'site';`
: The template controller needs to know what template you want to use. It'll automatically load the view defined in this variable and assign the view object to it.
`$this->template->message = 'hello, world!';`
: `$this->template` is a reference to the view object for our site template. What we're doing here is assigning a variable called "message", with a value of "hello, world!" to the view.
Now lets try running our code...
![Hello, World!](hello_world_2_error.png "Hello, World!")
For some reason Kohana's thrown a wobbly and isn't showing our amazing message.
If we look at the error message we can see that the View library wasn't able to find our site template, probably because we haven't made it yet *doh*!
Let's go and make the view file `application/views/site.php` for our message:
<html>
<head>
<title>We've got a message for you!</title>
<style type="text/css">
body {font-family: Georgia;}
h1 {font-style: italic;}
</style>
</head>
<body>
<h1><?php echo $message; ?></h1>
<p>We just wanted to say it! :)</p>
</body>
</html>
If we refresh the page then we can see the fruits of our labour:
![hello, world! We just wanted to say it!](hello_world_2.png "hello, world! We just wanted to say it!")
## Stage 3 Profit!
In this tutorial you've learnt how to create a controller and use a view to separate your logic from your display.
This is obviously a very basic introduction to working with Kohana and doesn't even scrape the potential you have when developing applications with it.

View File

@@ -0,0 +1,54 @@
# 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.
[!!] 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.
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.
For instance, by default the directories are set up like this:
www/
index.php
application/
modules/
system/
You could move the directories out of the web root so they look like this:
application/
modules/
system/
www/
index.php
Then you would need to change the settings in `index.php` to be:
$application = '../application';
$modules = '../modules';
$system = '../system';
## 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):
apps/
foobar/
application/
www/
bazbar/
application/
www/
kohana/
3.0.6/
3.0.7/
3.0.8/
modules/
And you would need to change the settings in `index.php` to be:
$application = '../application';
$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.

View File

@@ -0,0 +1 @@
Simple example of controller model and view working together.

View File

@@ -0,0 +1,7 @@
Making a template driven site.
<http://kerkness.ca/wiki/doku.php?id=template-site:create_the_template>
<http://kerkness.ca/wiki/doku.php?id=template-site:extending_the_template_controller>
<http://kerkness.ca/wiki/doku.php?id=template-site:basic_page_controller>

View File

@@ -0,0 +1,5 @@
<http://kerkness.ca/wiki/doku.php?id=routing:static_pages>
<http://kerkness.ca/wiki/doku.php?id=routing:multi-language_with_a_route>

View File

@@ -0,0 +1,93 @@
# Migrating from 3.0.x
## Request/Response
The request class has been split into a request and response class. To set the response body, you used to do:
$this->request->response = 'foo';
This has changed to:
$this->response->body('foo');
Some properties that existed in the request class have been converted into methods:
- Request::$controller -> Request::controller()
- Request::$action -> Request::action()
- Request::$directory -> Request::directory()
- Request::$uri -> Request::uri()
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().
## Validation
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.
### Context
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.
Old usage:
rule('password', 'matches', array('repeat_password'))
New usage:
rule('password', 'matches', array(':validation', 'password', 'repeat_password'))
The third parameter now contains all parameters that get passed to the rule. If you look at Valid::matches(), you'll see:
public static function matches($array, $field, $match)
: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)
: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:
- :validation - The validation object
- :field - The field being compared (rule('username', 'min_length', array(':field', 4)))
- :value - The value of the field
You can use any php function as a rule if it returns a boolean value.
### Filters
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.
## Cookie salts
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:
Cookie::$salt = 'foobar';
Or define an extended cookie class in your application:
class Cookie extends Kohana_Cookie
{
public static $salt = 'foobar';
}
## Controller constructor
If for some reason you are overloading your controller's constructor, it has changed to:
public function __construct(Request $request, Response $response)
## index.php / bootstrap.php changes
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.
## 404 Handling
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).
## Form Class
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).
## Logging
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:
- Kohana::ERROR -> Log::ERROR
- Kohana::DEBUG -> Log::DEBUG
- Kohana::INFO -> Log::INFO