kohana-3.2-documentation


kohana-3.2-documentation

 

Kohana 3.2 Documentation Official documentation from Kohana into one page. Compiled by Xavi Esteve, last updated Friday, 24 February 2012. What is Kohana? Kohana is an open source, object oriented MVC web framework built using PHP5 by a team of volunteers that aims to be swift, secure, and small. Kohana is licensed under a BSD 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 design, little or no configuration is necessary, error handling helps locate the source of errors quickly, and debugging and profiling provide insight into the application. To help secure your applications, tools for input validation, signed cookies, form and HTML generators are all included. The database layer provides protection against 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, make your changes, and send a pull request. If you are not familiar with git, you can also submit a feature request (requires registration). Unofficial Documentation If you are having trouble finding an answer here, have a look through the unofficial wiki. Your answer may also be found by searching the forum or stackoverflow followed by asking your question on either. Additionally, you can chat with the community of developers on the freenode #kohana IRC channel. Installation 1. Download the latest stable release from the Kohana website. 2. Unzip the downloaded package to create a !"#$%$ directory. 3. Upload the contents of this folder to your webserver. 4. Open $&&'()$*("%+,""*-*.$&/&#& and make the following changes: Set the default timezone for your application. Set the ,$-012.' in the Kohana::init call to reflect the location of the kohana folder on your server relative to the document root. 5. Make sure the $&&'()$*("%+)$)#0 and $&&'()$*("%+'"3- directories are writable by the web server. 6. Test your installation by opening the URL you set as the ,$-012.' 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 4(%56/67*8&06567090)6)#:"56;<==6>?6A 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. Once your install page reports that your environment is set up correctly you need to either rename or delete !"#$%&&'()( in the root directory. Kohana is now installed and you should see the output of the welcome controller: Installing Kohana 3.1 From GitHub The source code for Kohana 3.1 is hosted with GitHub. To install Kohana using the github source code first you need to install git. Visit 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 tutorial. 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. 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 *&%##+# directory. This may be at any level in the cascading filesystem. 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 bracketing. Curly Brackets Curly brackets are placed on their own line, indented to the same level as the control statement. !!"#$%%&'( )*"+,-"...",/0 1 """"222 3 &45& 1 """"222 3 !!"67'$%%&'( )*"+,-"...",/0"1 """"222 3"&45&"1 """"222 3 Class Brackets The only exception to the curly bracket rule is, the opening bracket of a class goes on the same line. !!"#$%%&'( '4-55"8$$"1 !!"67'$%%&'( '4-55"8$$ 1 Empty Brackets Don't put any characters inside empty brackets. !!"#$%%&'( '4-55"8$$"13 !!"67'$%%&'( '4-55"8$$"1"3 Array Brackets Arrays may be single line or multi-line. -%%-9+:-:".;":/:<":':".;":=:0 -%%-9+ """":-:".;":/:<" """":':".;":=:< 0 Opening Parenthesis The opening array parenthesis goes on the same line. !!"#$%%&'( )%%)*+ """",,, - !!"./'$%%&'(0 )%%)* + """",,, - 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. !!"#$%%&'( 1)%%)*"2")%%)*+ """",,, - !!"./'$%%&'( 1)%%)*"2")%%)*+ """",,, """"- Multidimensional The nested array is indented one tab to the right, following the single dimension rules. !!"#$%%&'( )%%)*+ """"3)%%3"24")%%)*+ """""""",,, """"-5 """"3)%%3"24")%%)*+ """""""",,, """"-5 - )%%)*+ """"3)%%3"24")%%)*+,,,-5 """"3)%%3"24")%%)*+,,,-5 - Arrays as Function Arguments !!"#$%%&'( 6$+)%%)*+ """",,, -- !!"./'$%%&'( 6$+)%%)*+ """",,, """"-- As noted at the start of the array bracket section, single line syntax is also valid. !!"#$%%&'( 6$+)%%)*+,,,-- !!"#$%&'()%*+&",-'".')//*(0"$-(0"$*(&1 2-345)'6"7%8*1"*1")"+&'9"$-(0"$*(&76 """")'')93:::;;< Naming Conventions Kohana uses under_score naming, not camelCase naming. Classes !!"=-(%'-$$&'">$)116"?1&1"=-(%'-$$&'"/'&,*A >$)11"=-(%'-$$&'#//$&"&A%&(21"=-(%'-$$&'"B !!"C-2&$">$)116"?1&1"C-2&$"/'&,*A >$)11"C-2&$=8&&1&"&A%&(21"C-2&$"B !!"D&0?$)'">$)11 >$)11"E&)(?%"B When creating an instance of a class, don't use parentheses if you're not passing something on to the constructor: !!"=-''&>%F 425"G"(&."H)%)5)1&< !!"I(>-''&>%F 425"G"(&."H)%)5)1&3;< Functions and Methods Functions should be all lowercase, and use under_scores to separate words: ,?(>%*-("2'*(J5&+&')0&345&+&')0&; B Variables All variables should be lowercase and use under_score, not camelCase: !!"=-''&>%F 4,--"G"75)'7< 4$-(0&A)K/$&"G"7?1&1"?(2&'1>-'&17< !!"I(>-''&>%F 4.&H-(%L)(%M8*1"G"7?(2&'1%--2N7< 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. 4%&A%"G"7%8*1"*1")"$-(0"%&A%"5$->J"%8)%"*1".')//&2:"O-'K)$$96".&")*K",-'"7 """""":7.')//*(0")%"PQ">8)'1:"R&'%*>)$")$*0(K&(%"*1"+&'9"*K/-'%)(%",-'"7 """""":7>-2&"'&)2)5*$*%9:"D&K&K5&'"%8)%")$$"*(2&(%)%*-("*1"2-(&".*%8"%)5167 """""":75?%"+&'%*>)$")$*0(K&(%"18-?$2"5&">-K/$&%&2".*%8"1/)>&16"),%&'"7 """""":7*(2&(%*(0".*%8"%)51:7< String Concatenation Do not put spaces around the concatenation operator: !!"=-''&>%F 41%'"G"7-(&7:4+)':7%.-7< !!"#$%&''(%)* +,)'"-".&$(./"+01'"/.)2&.3 +,)'"-".&$(."/"+01'"/".)2&.3 Single Line Statements Single-line IF statements should only be used when breaking normal execution (e.g. return or continue): !!"4%%(5)167(* 89":+9&&"--"+61'; """"'()<'$"+9&&3 89":+9&&"--"+61'; """"%&$)8$<(3 89":+9&&"--"+61'; """"6'(1=3 89":+9&&"--"+61'; """")>'&2"$(2"?%(5)8&$:.A&<",%'(2(B"<5C.;3 !!"D&)"1%%(5)167(* 89":+61E"--"+6<$; """"+61E"-"+61'"F"G3 Comparison Operations Please use OR and AND for comparison: !!"H&''(%)* 89"::+9&&"4DI"+61';"JK":+6"4DI"+%;; !!"#$%&''(%)* 89"::+9&&"LL"+61';"MM":+6"LL"+%;; Please use elseif, not else if: !!"H&''(%)* (7,(89":+61'; !!"#$%&''(%)* (7,("89:+61'; 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. ,28)%>":+01'; N """"%1,(".61'.* """"%1,(".9&&.* """"""""(%>&".>(77&.3 """"6'(1=3 """"%1,("O* """"""""(%>&".&$(.3 """"6'(1=3 """"B(91<7)* """"""""(%>&".6P(.3 """"6'(1=3 Q 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. !!"#$%%&'() *+",-+$$".."-/0%1 *+","2"-+$$1 !!"34'$%%&'() *+,-+$$".."-/0%1 *+,2-+$$1 *+",,*4(1"-+$$1 *+","-+$$".."-/0%"1 *+",2"-+$$1 Ternaries All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables. -+$$".",-/0%".."-+$$1"5"-+$$")"-/0%6 -+$$"."-/0%"5"-+$$")"-/0%6 All comparisons and operations must be done inside of a parentheses group: -+$$".",-/0%"7"81"5",-/0%"9"-+$$1")":(%;&4,-/0%16 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: -+$$".",-/0%".."-+$$1 """""5"-+$$ """"")"-/0%6 Type Casting Type casting should be done with spaces on each side of the cast: !!"#$%%&'() -+$$".",:(%*4<1"-/0%6 *+",",:(%*4<1"-/0%1 !!"34'$%%&'() -+$$".",:(%*4<1-/0%6 When possible, please use type casting instead of ternary operations: !!"#$%%&'() -+$$".",/$$;1"-/0%6 !!"34'$%%&'() -+$$".",-/0%".."=>?1"5"=>?")"ABCD6 When casting type to integer or boolean, use the short format: !!"#$%%&'() -+$$".",*4(1"-/0%6 -+$$".",/$$;1"-/0%6 !!"34'$%%&'() -+$$".",*4(&<&%1"-/0%6 -+$$".",/$$;&041"-/0%6 Constants Always use uppercase for constants: !!"#$%%&'() *&+,-&./012#3456746/8"/9:2;<=>&/? A<"B"6CDE AF"B"4DGG !!"H-'$%%&'() *&+,-&./0:#$-I(<-(/8"/9:2;<=>&/? A<"B"6%>& AF"B"->== Place constant comparisons at the end of tests: !!"#$%%&'() ,+".A+$$"JBB"K7G5E? !!"H-'$%%&'() ,+".K7G5E"JBB"A+$$? 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: ,+";<%,<F=&"A+$$",I"-$("&L<'(=:"K7G5E And the incorrect example would read: ,+"K7G5E",I"-$("&L<'(=:";<%,<F=&"A+$$ 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 #. !!"#$%%&'( !!H-'$%%&'( !!",-'$%%&'( M"H-'$%%&'( Regular Expressions When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster. !!"#$%%&'() ,+".N%&O29<('P./!<F'!,/8"AI(%?? !!"H-'$%%&'() ,+".&%&O,./<F'/8"AI(%?? 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 or \t, etc. !!"#$%%&'() N%&O29<('P./!<F'!/8"AI(%? !!"H-'$%%&'() N%&O29<('P.Q!<F'!Q8"AI(%? When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over . !!"#$%%&'() !"#$%"#!&'(#)*+),-./0-1&&'"+*20*340#5"1*20367"/8 ++09:(1""#(7; !"#$%"#!&'(#)*+),-./0-1&&'"+*20*,,40#5"1*20367"/8 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. 367"0<0=#>'?&#A'>!&#B(1>,:=8 !"#$%>'7(C)*+DB.B.3+*20367"/800++0EFGH !"#$%>'7(C)*+DB.B.3+I*20367"/80++0JKLMH http://kohanaframework.org/guide/about.mvc Discuss the MVC pattern, as it pertains to Kohana. Perhaps have an image, etc. 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: (&'660N1-#&%O1670#A7#:-60N1-#& P 0000!5Q&?(0R5:(7?1:0-1%675RR)/ 0000P 00000000++0EC?60?60SC#"#0T150-10-1>'?:0&1$?(BBB 0000U U If you want database access, have your model extend the Model_Database class: (&'660N1-#&%O1670#A7#:-60N1-#&%I'7'Q'6# P 0000!5Q&?(0R5:(7?1:0-1%675RR)/ 0000P 00000000++0EC?60?60SC#"#0T150-10-1>'?:0&1$?(BBB 0000U 0000!5Q&?(0R5:(7?1:0$#7%675RR)/ 0000P 00000000++0V#70675RR0R"1>07C#0-'7'Q'6#; 00000000"#75":037C?6WX-QWXY5#"T)BBB/8 0000U U If you want CRUD/ORM capabilities, see the ORM Module 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 !"#$% directory of the filesystem. You can also create sub-directories within the !"#$% directory to organize your files. All of the following examples are reasonable view files: &'''&()*!"#$%*+,-#./+/ &'''&()*!"#$%*/01#%*02,34./+/ &'''&()*!"#$%*/5,6374%*6#40"8%./+/ 9:;'&()*#55,5*!"#$%*#55,5%*<=<./+/ 9:;'&()*7,--,>*!"#$%*4#-/804#./+/ Loading Views View objects will typically be created inside a Controller using the View::factory method. Typically the view is then assigned as the Request::$response property or to another view. /328"7?3>74",>?074",>A02,34BC D ????E4+"%FG5#%/,>%#FG2,6HBI"#$JJ074,5HBK/01#%*02,34KCCL M 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. /328"7?3>74",>?074",>A">6#NBC D ????E!"#$?O?I"#$JJ074,5HBK/01#%*02,34KCL ????**?P#>6#5?4+#?!"#$ ????E02,34A/01#?O?E!"#$FG5#>6#5BCL ????**?:5?Q3%4?4H/#?70%4?"4?4,?0?%45">1 ????E02,34A/01#?O?B%45">1C?E!"#$L ????E4+"%FG5#%/,>%#FG2,6HBE02,34A/01#CL M Variables in Views Once view has been loaded, variables can be assigned to it using the View::set and View::bind methods. /328"7?3>74",>?074",>A5,0645"/BC D ????E!"#$?O?I"#$JJ074,5HBK3%#5*5,0645"/KC ????????FG%#4BK/807#%KR?0550HBKP,-#KR?K'05"%KR?KS,>6,>KR?KT#$?U,5VKR?K(,VH,KCCL ????????FG2">6BK3%#5KR?E4+"%FG3%#5CL ????**?(+#?!"#$?$"88?+0!#?E/807#%?0>6?E3%#5?!05"028#% ????E4+"%FG5#%/,>%#FG2,6HBE!"#$CL M The only difference between %#4BC and 2">6BC is that 2">6BC assigns the variable by reference. If you 2">6BC a variable before it has been defined, the variable will be created with a value of TWSS. You can also assign variables directly to the View object. This is identical to calling %#4BC; /328"7?3>74",>?074",>A5,0645"/BC D ????E!"#$?O?I"#$JJ074,5HBK3%#5*5,0645"/KCL ????E!"#$FG/807#%?O?0550HBKP,-#KR?K'05"%KR?KS,>6,>KR?KT#$?U,5VKR?K(,VH,KCL ????E!"#$FG3%#5?O?E4+"%FG3%#5L !!!!""!#$%!&'%(!('))!$*&%!+,)*-%.!*/0!+1.%2!&*2'*3)%. !!!!+4$'.562%.,7/.%5637089+&'%(:; < 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. ""!=..'>/!+,*>%?4'4)%!47!*))!&'%(. '%(AA3'/0?>)73*)9B,*>%?4'4)%BC!+,*>%?4'4)%:; If the application has three views that are rendered for the home page: 4%D,)*4%, 4%D,)*4%".'0%3*2, and ,*>%."$7D%. First, an abstract controller to create the template will be created: *3.42*-4!-)*..!E7/427))%2?F%3.'4%!%G4%/0.!E7/427))%2?#%D,)*4%!H !!!!,13)'-!+,*>%?4'4)%; !!!!,13)'-!I1/-4'7/!3%I72%9: !!!!H !!!!!!!!,*2%/4AA3%I72%9:; !!!!!!!!""!J*K%!+,*>%?4'4)%!*&*')*3)%!47!*))!&'%(. !!!!!!!!'%(AA3'/0?>)73*)9B,*>%?4'4)%BC!+4$'.56,*>%?4'4)%:; !!!!!!!!""!L7*0!+.'0%3*2!'/47!4$%!4%D,)*4%!*.!*!&'%( !!!!!!!!+4$'.564%D,)*4%56.'0%3*2!M!'%(AAI*-47289B4%D,)*4%".'0%3*2B:; !!!!< < Next, the home controller will extend E7/427))%2?F%3.'4%: -)*..!E7/427))%2?N7D%!%G4%/0.!E7/427))%2?F%3.'4%!H !!!!,13)'-!I1/-4'7/!*-4'7/?'/0%G9: !!!!H !!!!!!!!+4$'.56,*>%?4'4)%!M!BN7D%B; !!!!!!!!+4$'.564%D,)*4%56-7/4%/4!M!'%(AAI*-47289B,*>%."$7D%B:; !!!!< < 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: ""!O/!8712!&'%(!I')%A ""!P/)8!4$%!+1.%2!&*2'*3)%!('))!3%!*&*')*3)%!'/!Q&'%(."1.%2")7>'/R,$,Q ST,$,!%-$7!'%(AAI*-47289B1.%2")7>'/B:563'/09B1.%2BC!+1.%2:!T6 The other option is to include the view directly, which makes all of the current variables available to the included view: ""!O/!8712!&'%(!I')%A ""!=/8!&*2'*3)%!0%I'/%0!'/!4$'.!&'%(!('))!3%!'/-)10%0!'/!Q&'%(."D%..*>%R,$,Q ST,$,!'/-)10%!U7$*/*AAI'/0?I')%9B&'%(.BC!B1.%2")7>'/B:!T6 You can also assign a variable of your parent view to be the child view from within your controller. For example: ""!O/!8712!-7/427))%2A !"#$%&'(")&*%)'+&*%,)-%)./012 3 ''''45%/6'7'8%/699(+&*,:;1<&,==,)>*/=!$+*/2? ''''45%/6A*%*$/'7'BC,=/'*%*$/B? ''''45%/6A#,.;'7'8%/699(+&*,:;1<!+D/E>(,,#+:<2? F >>'G)'5%/6E>&,==,)>*/=!$+*/H!I!9 JI*=$A JI/+.A ''''J*%*$/AJK!I!'/&I,'4*%*$/AJ>*%*$/A J>I/+.A J#,.;A ''''JK!I!'/&I,'4#,.;'KA J>#,.;A J>I*=$A Of course, you can also load an entire Request within a view: JK!I!'/&I,'L/M"/E*99(+&*,:;1<"E/:>$,D%)<2A/0/&"*/12'KA This is an example of [HMVC], which makes it possible to create and read calls to other URLs within your application. 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 NOOONPQ in %)./0H!I!. The default value is +!!$%&+*%,). 2. Module Paths This is set as an associative array using Kohana::modules in NOOONPQ>#,,*E*:+!H!I!. Each of the values of the array will be searched in the order that the modules are defined. 3. System Path Defined as CRCONPQ in %)./0H!I!. The default value is E;E*/=. 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: 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 !"#$%$&'(("()*#* view, So it would call +"#$%$,,-.%/0-.1'234.'563783!"#$%$&'(("(39. This would return $**1.:$;."%&4.'56&!"#$%$&'(("()*#* because it takes precidence over 6<6;'=&4.'56&!"#$%$&'(("()*#*. By doing this we can change the error view without editing the system folder. If we used >.'5,,-$:;"(<235'1:"='39 it would call +"#$%$,,-.%/0-.1'234.'563735'1:"='39 which would return $**1.:$;."%&4.'56&5'1:"=')*#* because it takes precidence over ="/?1'6&:"=="%&4.'56&5'1:"=')*#*. 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 +"#$%$,,-.%/0-.1'23:1$66'63783:""!.'39 which will return $**1.:$;."%&:1$66'6&:""!.')*#*. Assuming Cookie extends Kohana_Cookie, the autoloader would then call +"#$%$,,-.%/0-.1'23:1$66'6373!"#$%$&:""!.'39 which will return !"!#$%&'()!!$!&*+,)-)&'++*.$/0,0 because that file does not exist anywhere higher in the cascade. This is an example of transparent extension. If you used 1.$2334)'#+5"678!$579 it would call :+,)-)334.-;<4.($67=.$2!7>78!$579 which would return %+;8($!&'+%%+-&=.$2!&8!$5/0,0. If we wanted to change something in '+-4.?&;)#))!$/0,0 we could copy the file to )00(.')#.+-&'+-4.?&;)#))!$/0,0 and make the changes there. Keep in mind that config files are merged 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 should be stored here. This includes controllers, models, and all other classes. All classes must follow the class naming conventions. 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 for more information. i18n/ Translation files return an associative array of strings. Translation is done using the <<69 method. To translate "Hello, world!" into Spanish, you would call <<67A$((+>B2+5(;C79 with I18n::$lang set to "es-es". I18n files are merged rather than overwritten by the cascade. See I18n files 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 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 for more information. other You can include any other folders in your cascading filesystem. Examples include, but are not limited to, ?8.;$, =$-;+5, %$;.), whatever you want. For example, to find %$;.)&(+?+/0-? in the cascading filesystem you would call :+,)-)334.-;<4.($67%$;.)7>7(+?+7>70-?79. Finding Files The path to any file within the filesystem can be found by calling Kohana::find_file: &&BD.-;B#,$B48((B0)#,B#+BE'()!!$!&'++*.$/0,0E F0)#,BGB:+,)-)334.-;<4.($67'()!!$!7>B7'++*.$79H &&BD.-;B#,$B48((B0)#,B#+BE=.$2!&8!$5&(+?.-/0,0E F0)#,BGB:+,)-)334.-;<4.($67=.$2!7>B78!$5&(+?.-79H If the file doesn't have a /0,0 extension, pass the extension as the third param. &&BD.-;B#,$B48((B0)#,B#+BE?8.;$&%$-8/%;E F0)#,BGB:+,)-)334.-;<4.($67?8.;$7>B7%$-87>B7%;79H &&BI4BF-)%$B.!BEJKKKLKMLKML4.5!#L0+!#EB#,.!B2+8(;B(++*B4+5BE0+!#!&JKKKLKMLKML4.5!#L0+!#/#$N#.($E F0)#,BGB:+,)-)334.-;<4.($670+!#!7>BF-)%$>B7/#$N#.($79H 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, DOMPDF, Mustache and Swiftmailer. For example, if you wanted to use DOMPDF, you would copy it to )00(.')#.+-&=$-;+5&;+%0;4 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. !"#$%!"&'()*+*,,-%+./-%0"123"+.(!24&2.(56.-7.(56.-7.(56.-/8(+-%9242%+82:; Now you can use DOMPDF without loading any more files: <6.-&=&+">&?AB?C; If you want to convert views into PDFs using DOMPDF, try the PDFView module. 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 8(+-%97 directory, which return an associative array: DE6)6&."-%+".12FGFBHIJ2:&(!&.%"12K(&.%!"8L&M8!%6L&*88"MMN2:; !"L$!+&*!!*O1 &&&&2M"LL%+92&=P&23*0$"24 &&&&2(6L%(+M2&=P&*!!*O1 &&&&&&&&2-((2&=P&2Q*!24 &&&&:4 :; If the above configuration file was called 5O8(+-N6)6, you could access it using: <8(+-%9&=&'()*+*,,<8(+-%9RP0(*.125O8(+-2:; <(6L%(+M&=&<8(+-%9RP9"L12(6L%(+M2: Merge Configuration files are slightly different from most other files within the cascading filesystem 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. 77&8(+-%97%+-0"8L(!N6)6 DE6)6&."-%+".12FGFBHIJ2:&(!&.%"12K(&.%!"8L&M8!%6L&*88"MMN2:; !"L$!+&*!!*O1 &&&&2%!!"9$0*!2&=P&*!!*O1 &&&&&&&&2.%"2&=P&2.%8"24&77&.("M&+(L&"S%ML&%+&."-*$0L&8(+-%9&-%0" &&&&&&&&25($M"2&=P&25($M"M24&77&(3"!!%."M&25($M"2&=P&25%8"2&%+&L)"&."-*$0L&8(+-%9&-%0" :; 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 M%L"N6)6: 77&8(+-%97M%L"N6)6 DE6)6&."-%+".12FGFBHIJ2:&(!&.%"12K(&.%!"8L&M8!%6L&*88"MMN2:; !"L$!+&*!!*O1 &&&&2L%L0"2&=P&2$!&F)%+O&T"QM%L"24 &&&&2*+*0OL%8M2&=P&CHUFV4&77&*+*0OL%8M&8(."&9("M&)"!"4&M"L&L(&CHUFV&L(&.%M*Q0" :; We could now call '()*+*,,<8(+-%9RP0(*.12M%L"NL%L0"2: to get the site name, and '()*+*,,<8(+-%9R P0(*.12M%L"N*+*0OL%8M2: 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. !!"#$%&'(!)*+,'$%,-./. 01./."2*&'%*234565789:4;"$+"2'*34<$"2'+*#=",#+'.=">##*,,-4;? +*=+%">++>A3 """"4B-C-C4"DE">++>A3 """"""""4#$2*%>F*4"DE"4G+$(4H """"""""42$I%J$>24"DE"4&'J*,!$+>..KB-C-C-=>+-(L4H """"""""42$#F*%=>='$%4"DE"42$#,!B-C-C4H """"""""4+*J*>,*24"DE"4CM!CN!OCCP4H """"""""4',,*,4"DE"4J'%Q!=$!R(!=+>#Q*+4H """";H """"4B-B-C4"DE">++>A3 """"""""4#$2*%>F*4"DE"4S'L>+24H """"""""42$I%J$>24"DE"4&'J*,!$+>..KB-B-C-=>+-(L4H """"""""42$#F*%=>='$%4"DE"42$#,!B-B-C4H """"""""4+*J*>,*24"DE"4BC!BN!OCCP4H """"""""4',,*,4"DE"4J'%Q!=$!R(!=+>#Q*+4H """";H """"!!!"---"*=#"--- ;? You could then do the following: !!"T%"A$+"#$%=+$JJ*+ U)'*IKE)*+,'$%,"D"V$/>%>WWU#$%&'(KEJ$>234)*+,'$%,4;? !!"T%"A$+")'*IW &$+*>#/"3U)*+,'$%,">,"U)*+,'$%; X """"!!"*#/$",$F*"/=FJ"=$"2',.J>A"*>#/")*+,'$% Y 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: 01./."*#/$"ZZ34:*JJ$H"I$+J2[4;?1E 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: TB\%WWJ>%(34&+4;? This will change the language to 'es-es'. Defining language files To define the language file for the above language change, create a 'B\%!&+-./. that contains: !"#$# %&'(%)*+%%+, - ****./&0012*31%045.*67*.81)91(%2*:1)4&5.2 ;< Now when you do ==-./&0012*31%045.;, you will get 81)91(%2*:1)4&5 I18n variables You can define variables in your __() calls like so: &>$1*==-./&0012*?(&%.2*+%%+,-.?(&%.*67*A(&%)+:&;;< Your i18n key in your translation file will need to be defined as: !"#$# %&'(%)*+%%+, - ****./&0012*?(&%.*67*.81)91(%2*?(&%.2 ;< Defining your own __() function You can define your own __() function by simply defining your own i18n class: !"#$# >0+*BCD)*&E'&)4*F1$+)+=BCD) G ****HH*B)'&)'I1)+00,*&:#', J K()>'I1)*==-A'%I)L2*+%%+,*AM+0(&*6*NOPP2*A0+)L*6*.&)Q(.; G ****HH*R1(%*K()>'I1)+0I',*$&%& J This will cause the built-in __() function to be ignored. 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: F1$+)+??:&+L&-.K1%:.2*.K11S+%.;< This will look in the :&+L&HK1%:T#$# file for the K11S+% key: !"#$# %&'(%)*+%%+,- ****.K11S+%.*67*./&0012*31%045.2 ;< You can also look in subfolders and sub-keys: F1$+)+??:&+L&-.K1%:H>1)'+>'.2*.K11S+%TS+%.;< This will look in the !"##$%"#&'()!#&*(+,$*,-./. for the 0'((1$)201$)2 key: 34./. )",5)+6$))$78 66669'((1$)96:;6$))$78 6666666691$)96:;69<"==(>6?()=A9> 6666B> BC 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 classes and views. Configuration By default kohana is setup to load configuration values from config files in the cascading filesystem. However, it is very easy to adapt it to load config values in other locations/formats. Sources The system is designed around the concept of Config Sources, which loosely means a method of storing configuration values. To read config from a source you need a Config Reader. Similarly, to write config to a source you need a Config Writer. Implementing them is as simpe as extending the Kohana_Config_Reader / Kohana_Config_Writer interfaces: *=$##6D(/$+$EF(+'G%EH$,$1$#"EI"$")6G!.="!"+,#6D(/$+$EF(+'G%EI"$") *=$##6D(/$+$EF(+'G%EH$,$1$#"EJ)G,")6"K,"+#6D(/$+$EF(+'G%EH$,$1$#"EI"$")6G!.="!"+,#6D(/$+$EF(+'G%EJ) You'll notice in the above example that the Database Writer extends the Database Reader. This is the convention with config sources, the reasoning being that if you can write to a source chances are you can also read from it as well. However, this convention is not enforced and is left to the developer's discretion. Groups In order to aide organisation config values are split up into logical "groups". For example database related settings go in a $,$1$#" group, and session related settings go in a #"##G(+ group. How these groups are stored/organised is up to the config source. For example the file source puts different config groups into different files ($,$1$#"-./., #"##G(+-./.) whereas the database source uses a column to distinguish between groups. To load a config group simply call D(/$+$LLM*(+'G%N;=($8B with the name of the group you wish to load: M*(+'G%6:6D(/$+$LLM*(+'G%N;=($89!7E%)(5.9BC =($8B will return an instance of Config_Group which encapsulates the config values and ensures that any modifications made will be passed back to the config writers. To get a config value from a Config_Group object simply call Config_Group::get: M*(+'G%6:6D(/$+$LLM*(+'G%N;=($89!7E%)(5.9BC MO$=5"66:6M*(+'G%N;%",89O$)9BC To modify a value call Config_Group::set: M*(+'G%6:6D(/$+$LLM*(+'G%N;=($89!7E%)(5.9BC M*(+'G%N;#",89O$)9>69+"?EO$=5"9BC Alternative methods for getting / setting config In addition to the methods described above you can also access config values using dots to outline a path from the config group to the value you want: !!"#$%&'("&')*+",-.-/-0*1232 4*.54%"-44-67 """"8,*&-5).8"9:"-44-67 """"""""8;$%%*;.'$%8"9:"-44-67 """"""""""""83$0.%-<*8"9:"8)$;-)3$0.8 """"""""= """"= => !!"#$,*"?3';3"%**,0"3$0.%-<*+ 3$0.%-<*"9"A$3-%-++;$%&'(B:)$-,78,-.-/-0*1,*&-5).1;$%%*;.'$%13$0.%-<*8=> Which is equivalent to: ;$%&'("9"A$3-%-++;$%&'(B:)$-,78,-.-/-0*8=B:(*.78,*&-5).8=> 3$0.%-<*"9";$%&'(C8;$%%*;.'$%8DC83$0.%-<*8D> Obviously this method is a lot more compact than the original, however please bear in mind that using ,$.1%$.-.'$% is a lot slower than calling (*.7= and traversing the array yourself. Dot notation can be useful if you only need one specific variable, but otherwise it's best to use (*.7=. As Config_Group extends Array_Object you can also use array syntax to get/set config vars: ;$%&'("9"A$3-%-++;$%&'(B:)$-,78,-.-/-0*8=> !!"E*..'%(".3*"F-4 3$0.%-<*"9";$%&'(C8,*&-5).8DC8;$%%*;.'$%8DC83$0.%-<*8D> !!"G*..'%(".3*"F-4 ;$%&'(C8,*&-5).8DC8;$%%*;.'$%8DC83$0.%-<*8D"9"8HIJ1K1K1H8> Again, this syntax is more costly than calling (*.7= / 0*.7=. Config Merging One of the useful features of the config system is config group merging. This works in a similar way to the cascading filesystem, with configuration from lower sources lower down the source stack being merged with sources further up the stack. If two sources contain the same config variables then the one from the source further up the stack will override the one from the "lower" source. However, if the source from higher up the stack does not contain a particular config variable but a source lower down the stack does then the value from the lower source will be used. The position of sources in the stack is determined by how they are loaded in your bootstrap. By default when you load a source it is pushed to the top of a stack: !!"G.-;L+"M*<2.6: A$3-%-++;$%&'(B:-..-;37%*?"#$%&'(NO')*=> !!"G.-;L+"#$%&'(NO')* A$3-%-++;$%&'(B:-..-;37%*?"#$%&'(NP-.-/-0*=> !!"G.-;L+"#$%&'(NP-.-/-0*Q"#$%&'(NO')* In the example above, any config values found in the database will override those found in the filesystem. For example, using the setup outlined above: !!"#$%&'(54-.'$%"'%".3*"&')*060.*<+ """"*<-')+ """"""""0*%,*4+" """"""""""""*<-')+"<61-?*0$<*1-,,4*00R*S-<2)*1;$< """"""""""""%-<*+""T%L%$?% """"""""<*.3$,+"0<.2 !!"#$%&'()*+,'$%"'%",-."/+,+0+1.2 """".3+'42 """"""""1.%/.*2 """""""""""".3+'42"3561)7.*8$$46+//*.119(3+'468$3 """"""""""""%+3.2"":$-+%+";$, !!"#$%&'()*+,'$%"*.,)*%./"05":$-+%+22<8$%&'(=>4$+/?.3+'4A """".3+'42 """"""""1.%/.*2 """""""""""".3+'42"3561)7.*8$$46+//*.119(3+'468$3 """"""""""""%+3.2"":$-+%+";$, """"""""3.,-$/2"13,7 Note: The above syntax is simply pseudo code to illustrate the concept of config merging. On some occasions you may want to append a config source to the bottom of the stack, to do this pass BCDEF as the second parameter to +,,+8-?A: !!"E,+8G2"H.37,5> :$-+%+22<8$%&'(=>+,,+8-?%.I"#$%&'(JB'4.AK !!"E,+8G2"#$%&'(JB'4. :$-+%+22<8$%&'(=>+,,+8-?%.I"#$%&'(JL+,+0+1.M"BCDEFAK !!"E,+8G2"#$%&'(JB'4.M"#$%&'(JL+,+0+1. In this example, any values found in the filesystem will override those found in the db. For example: !!"#$%&'()*+,'$%"'%",-."&'4.151,.32 """".3+'42 """"""""1.%/.*2" """""""""""".3+'42"356+I.1$3.6+//*.119.N+374.68$3 """"""""""""%+3.2""O%G%$I% """"""""3.,-$/2"13,7 !!"#$%&'()*+,'$%"'%",-."/+,+0+1.2 """".3+'42 """"""""1.%/.*2 """""""""""".3+'42"3561)7.*8$$46+//*.119(3+'468$3 """"""""""""%+3.2"":$-+%+";$, !!"#$%&'()*+,'$%"*.,)*%./"05":$-+%+22<8$%&'(=>4$+/?.3+'4A """".3+'42 """"""""1.%/.*2 """""""""""".3+'42"356+I.1$3.6+//*.119.N+374.68$3 """"""""""""%+3.2""O%G%$I% """"""""3.,-$/2"13,7 Using different config sources based on the environment In some situation's you'll need to use different config values depending on which state :$-+%+22<.%P'*$%3.%, is in. Unit testing is a prime example of such a situation. Most setups have two databases; one for normal development and a separate one for unit testing (to isolate the tests from your development). In this case you still need access to the config settings stored in the 8$%&'( directory as it contains generic settings that are needed whatever environment your application is in (e.g. encryption settings), )o replacing the default #$%&'(JB'4. source isn't really an option. To get around this you can attach a separate config file reader which loads its config from a subdir of 8$%&'( called "testing": :$-+%+22<8$%&'(=>+,,+8-?%.I"#$%&'(JB'4.AK :$-+%+22<8$%&'(=>+,,+8-?%.I"#$%&'(JL+,+0+1.AK '&"?:$-+%+22<.%P'*$%3.%,"QQQ":$-+%+22RFERSTUA V !!!!"#$%&%''()#&*+,-.%//%)$0&12!3#&*+,45+6107)#&*+,8/19/+&,7::; < During normal development the config source stack looks like 3#&*+,4=%/%>%91?!3#&*+,45+6107)#&*+,7:. However, when "#$%&%''(1&+A#&B1&/!CCC!"#$%&%''DEFDGHI the stack looks like 3#&*+,45+6107)#&*+,8/19/+&,7:?!3#&*+,4=%/%>%91?!3#&*+,45+6107)#&*+,7: Request Flow Every application follows the same flow: 1. Application starts from +&J1KLM$M. 1. The application, module, and system paths are set. (NOOONDP, QR=ONDP, and FSFONDP) 2. Error reporting levels are set. 3. Install file is loaded, if it exists. 4. The bootstrap file, NOOONDP8>##/9/A%MLM$M, is included. 2. Once we are in >##/9/A%MLM$M: 1. The Kohana class is loaded. 2. Kohana::init is called, which sets up error handling, caching, and logging. 3. Kohana_Config readers and Kohana_Log writers are attached. 4. Kohana::modules is called to enable additional modules. Module paths are added to the cascading filesystem. Includes each module's +&+/LM$M file, if it exists. The +&+/LM$M file can perform additional environment setup, including adding routes. 5. Route::set is called multiple times to define the application routes. 6. 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. 3. Application flow returns to index.php 1. The main Request response is displayed Bootstrap The bootstrap is located at %MM6+)%/+#&8>##/9/A%MLM$M. It is responsible for setting up the Kohana environment and executing the main response. It is included by +&J1KLM$M (see Request flow) The bootstrap is responsible for the flow of your application. In previous versions of Kohana the bootstrap was in 9T9/1B 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 works. You could add any other settings that all your application needed here. 88!F%BM61!1K)1AM/!*A#B!>##/9/A%MLM$M!2+/$!)#BB1&/9!/A+BB1J!J#2& 88!F1/!/$1!J1*%U6/!/+B1!V#&1L J%/14J1*%U6/4/+B1V#&1491/07NB1A+)%83$+)%,#7:; 88!F1/!/$1!J1*%U6/!6#)%61L 91/6#)%610W34NWW?!71&4XFLU/*-Y7:; 88!E&%>61!/$1!"#$%&%!%U/#-6#%J1AL 9M64%U/#6#%J4A1,+9/1A0%AA%T07"#$%&%7?!7%U/#46#%J7::; 88!E&%>61!/$1!"#$%&%!%U/#-6#%J1A!*#A!U&91A+%6+V%/+#&L +&+491/07U&91A+%6+V14)%66>%)Z4*U&)7?!79M64%U/#6#%J4)%667:; Initialization and Configuration Kohana is then initialized by calling Kohana::init, and the log and config reader/writers are enabled. !!"#$%&'("()*(+&,"-+.%"/..,0,+$&1&2&"34,2"*.%%(5,0",+4%%(6"6.35 7.2$5$88454,9$++$:9; """"/$0(<=+';">?";!.2$5$!;A """"456()<-4'(">?"-$'0(A BBC !!"D,,$*2",2("-4'("3+4,(+",."'.EE45E1"F=',4&'("3+4,(+0"$+("0=&&.+,(61 7.2$5$88G'.EH?$,,$*295(3"7.2$5$<I.E<J4'(9DKKKDLM1;'.E0;BBC !!"D,,$*2"$"-4'("+($6(+",."*.5-4E1"F=',4&'("+($6(+0"$+("0=&&.+,(61 7.2$5$88G*.5-4EH?$,,$*295(3"7.2$5$<N.5-4E<J4'(BC 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 G<#OPQOPR;MLLK<MS#L;T and set caching, profiling, etc. accordingly. This is just an example, there are many different ways to accomplish the same thing. !!"O)*(+&,"-+.%"2,,&8!!E4,2=/1*.%!40$4$263!.2$5$&2&1*.%!/'./!-U$-(V(UV/!$&&'4*$,4.5!/..,0,+$&1&2& 111"R,+4%%(6T !WW "W"#(,",2("(5X4+.5%(5,"0,$,=0"/:",2("6.%$451 "W! 4-"90,+&.09G<#OPQOPR;MLLK<MS#L;TA";.2$5$&2&1*.%;B"Y>>"JDI#OB Z """"!!"[("$+("'4X(Y """"7.2$5$88G(5X4+.5%(5,">"7.2$5$88KPS\]NL^S_C """"!!"L=+5".--"5.,4*(0"$56"0,+4*,"(++.+0 """"(++.+<+(&.+,45E9O<DII"`"O<_SL^NO"`"O<#LP^NLBC a !WW "W"^54,4$'4b("7.2$5$A"0(,,45E",2("6(-$=',".&,4.501 "111"R,+4%%(6T "W! 7.2$5$88454,9$++$:9 """";/$0(<=+';""">?"7.2$5$88G(5X4+.5%(5,">>>"7.2$5$88KPS\]NL^S_"c";!;"8";!.2$5$&2&1*.%!;A """";*$*245E;"""">?"7.2$5$88G(5X4+.5%(5,">>>"7.2$5$88KPS\]NL^S_A """";&+.-4'(;"""">?"7.2$5$88G(5X4+.5%(5,"Y>>"7.2$5$88KPS\]NL^S_A """";456()<-4'(;">?"JDI#OA BBC 111"R,+4%%(6T Note: The default bootstrap will set 7.2$5$88G(5X4+.5%(5,">"G<O_QR;7SMD_D<O_Q;T if set. Docs on how to supply this variable are available in your web server's documentation (e.g. Apache, Lighttpd). This is considered better practice than many alternative methods to set 7.2$5$88G(5X4+.%(5,, as you can change the setting per server, without having to rely on config options or hostnames. Modules Read the Modules page for a more detailed description. 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. !!"O)$%&'("()*(+&,"-+.%"/..,0,+$&1&2& !"#$%$&&'"()*+,-$..$/- 00001($2$3$,+10004506789:;<=1($2$3$,+1> 00001".'1000000004506789:;<=1".'1> 00001),+.?)(+1004506789:;<=1),+.?)(+1> AAB Routes Read the Routing page for a more detailed description and more examples. Routes are then defined via Route::set(). CC0;#+0(+D$)*20.")2+02#$20E"'+,0F2#0!"#$%$0G H")2+&&,+2-1(+D$)*21>01-IE"%2."**+.5-CI$E2"%5-CI(5AAA1A 0000J5(+D$)*2,-$..$/- 000000001E"%2."**+.104501F+*E"'+1> 000000001$E2"%1000004501%(+K1> 0000AAB Modules Modules are simply an addition to the Cascading Filesystem. 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, 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 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 which is similar to the above sites. Enabling modules Modules are enabled by calling Kohana::modules and passing an array of 1%$'+104501L$2#1. The name isn't important, but the path obviously is. A module's path does not have to be in 6789:;<, but usually is. You can only call Kohana::modules once. !"#$%$&&'"()*+,-$..$/- 00001$)2#100000004506789:;<=1$)2#1>0000000CC0M$,E0$)2#+%2E$2"% 00001E$E#+10000004506789:;<=1E$E#+1>000000CC0N$E#%?0F2#0')*2L*+03$EO+%(, 00001E"(+3+%E#1004506789:;<=1E"(+3+%E#1>00CC0M+%E#'$.O%?02""* 00001($2$3$,+10004506789:;<=1($2$3$,+1>000CC08$2$3$,+0$EE+,, 00001'$?+10000004506789:;<=1'$?+1>000000CC0P'$?+0'$%L)*$2"% 00001".'1000000004506789:;<=1".'1>00000000CC073Q+E20H+*$2"%,#L06$LL%? 00001"$)2#10000004506789:;<=1"$)2#1>000000CC07:)2#0$)2#+%2E$2"% 00001L$?%$2"%104506789:;<=1L$?%$2"%1>0CC09$?%?0"D0.+,)*2, 00001)%22+,210004506789:;<=1)%22+,21>000CC0R%202+,2%? 00001),+.?)(+1004506789:;<=1),+.?)(+1>00CC0R,+.0?)(+0$%(0:9P0("E)'+%2$2"% 0000AAB Init.php When a module is activated, if an %2=L#L 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. 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 !"#$""%&'()*+,-) 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. You can look at examples of modules made by Kohana or other users. 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 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 .///.%0&1((2-23456575 you will see the "default" route as follows: $(*2,88-,29:),;4*+2:<=:9>?(23(++,3A9&>4?2B(A9&>B)ACCC:C DA),;4*+2-94334E9 ====:?(23(++,3:=FA=:G,+?(',:< ====:4?2B(:=====FA=:B),H:< CCI 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 ),;4*+2 that will match urls in the format of 9>?(23(++,3A9&>4?2B(A 9&>B)ACCC. Let's take a closer look at each of the parameters of Route::set, which are 4',, *3B, and an optional array 3,J,H. 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 >A are keys and anything surrounded with 9C are optional parts of the uri. In Kohana routes, any character is allowed and treated literally aside from 9C>A. 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 9>?(23(++,3A9&>4?2B(A9&>B)ACCC. 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) would be assumed resulting in the #(23(++,3KL,+?(', class being loaded and the 4?2B(KB),H 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 ?+4--,-&?(23(++,3 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 in its matching process. By default each key (surrounded by !") will match #$%&'()*+,-- (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, ./01+ and .22131.45. Because this route will only match urls that begin with ./01+ or .22131.45, the default route would still work for controllers in 63.7757%68+4983359. :8;45<<754=>756418+7>'?>!/1956489"=%!68+4983359"=%!.6418+"=%!1/"AAA>' ????.99.= ????????>/1956489>?B"?>=./01+C.22131.45A> ????AA ????D"/52.;347=.99.= ????????>68+4983359>?B"?>E805>' ????????>.6418+>?????B"?>1+/5F>' ????AA( You can also use a less restrictive regex to match unlimited parameters, or to ignore overflow in a route. In this example, the url 288G.9%G.H%.+/D.+4E1+I%5375J4E.4%17D8+D4E5%;93 would be routed to K8+4983359JL88G.9<<.6418+JG.H=A and the M74;22M parameter would be M.+/D.+4E1+I%5375J4E.4%17D8+D 4E5%;93M. If you wanted to use this for unlimited parameters, you could explode it, or you just ignore the overflow. :8;45<<754=>/52.;34>'?>=!68+4983359"=%!.6418+"=%!74;22"AAA>'?.99.=>74;22>?B"?>&N>AA ????D"/52.;347=.99.= ????????>68+4983359>?B"?>O536805>' ????????>.6418+>?B"?>1+/5F>' ??AA( 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 68+4983359 and .6418+ 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 K8+4983359JP536805<<.6418+J1+/5F=A would be called. If we called 288G.9 then only the default for action would be used, so it would call K8+4983359JL88G.9<<.6418+J1+/5F=A and finally, if we called 288G.9%G.H then neither default would be used and K8+4983359JL88G.9<<.6418+JG.H=A 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: :8;45<<754=>45741+I>'?2;+6418+=Q;91A ????R ????????12?=Q;91?BB?>288%G.9>A ????????????954;9+?.99.= ????????????????>68+4983359>?B"?>O536805>' !!!!!!!!!!!!!!!!"#$%&'("!!!!!)*!"+'',#-". !!!!!!!!!!!!/0 !!!!1. !!!!"+''2,#-" /0 As you can see in the below route, the reverse uri parameter might not make sense. 3'4%56675%8"%57%&(9".!+4($%&'(8:4-&/ !!!!; !!!!!!!!&+!8:4-&!))!"<2=#(94#95!-595>2*8?/"/ !!!!!!!!; !!!!!!!!!!!!A''B&56675%8"=#(94#95".!:C#%$DEFG/0 !!!!!!!!!!!!-5%4-(!#--#H8 !!!!!!!!!!!!!!!!"$'(%-'==5-"!)*!"I5=$'C5". !!!!!!!!!!!!!!!!"#$%&'("!!!!!)*!"+'',#-" !!!!!!!!!!!!/0 !!!!!!!!1 !!!!1. !!!!"<=#(94#95*2<-57%J'+J4-&* /0 If you are using php 5.2, you can still use callbacks for this behavior (this example omits the reverse route): 3'4%56675%8"%57%&(9".!#--#H8"A=#77".!"C5%D'KJ%'JL-'$577JCHJ4-&"//0 Examples There are countless other possibilities for routes. Here are some more examples: 2M !M!N4%D5(%&$#%&'(!7D'-%$4%7 !M2 3'4%56675%8"#4%D".!"<#$%&'(*". !!#--#H8 !!!!"#$%&'("!)*!"8='9&(O='9'4%/" !!// !!P*K5+#4=%78#--#H8 !!!!"$'(%-'==5-"!)*!"#4%D" !!//0 2M !M!Q4=%&P+'-C#%!+55K7 !M!!!RSTURV2$'CC5(%7?-77 !M!!!SUWU?X7'( !M2 3'4%56675%8"+55K7".!"<475-J&K*82<#$%&'(*/?<+'-C#%*". !!#--#H8 !!!!"475-J&K"!)*!"YK". !!!!"+'-C#%"!)*!"8-77O#%'COX7'(/". !!// !!P*K5+#4=%78#--#H8 !!!!"$'(%-'==5-"!)*!"+55K7". !!!!"#$%&'("!)*!"7%#%47". !!//0 2M !M!Z%#%&$!L#957 !M2 3'4%56675%8"7%#%&$".!"<L#%D*?D%C=". !!#--#H8 !!!!"L#%D"!)*!"E#P[NP\]P^J2G". !!// !!P*K5+#4=%78#--#H8 !!!!"$'(%-'==5-"!)*!"7%#%&$". !!!!"#$%&'("!)*!"&(+,-". !!//0 12 !2!3'4!+'("%!5&6,!75#78,79 !2!!!:+&%;#55,<=>?#8##7 !2!!!A#%$8>B#6,?'#<+&(C !21 D'4%,>>7,%E"C#55,<=".!"F#$%&'(*EF$'(%<'55,<*/>F&+*". !!#<<#=E !!!!"$'(%<'55,<"!)*!"GHIJKG#ILKMM". !!!!"#$%&'("!!!!!)*!"GHIJKG#ILKMM". !!// !!I*+,N#45%7E#<<#=E !!!!"$'(%<'55,<"!)*!"O5&+,78'B". !!//0 12 !2!P4&$6!7,#<$8 !21 D'4%,>>7,%E"7,#<$8".!">FQ4,<=*".!#<<#=E"Q4,<="!)*!"R2"// !!I*+,N#45%7E#<<#=E !!!!"$'(%<'55,<"!)*!"7,#<$8". !!!!"#$%&'("!)*!"&(+,-". !!//0 Request parameters The +&<,$%'<=, $'(%<'55,< and #$%&'( can be accessed from the Request as public properties like so: 11!S<'!B&%8&(!#!$'(%<'55,<> T%8&7I*<,Q4,7%I*#$%&'(E/0 T%8&7I*<,Q4,7%I*$'(%<'55,<E/0 T%8&7I*<,Q4,7%I*+&<,$%'<=E/0 11!U#(!?,!47,+!#(=B8,<,> D,Q4,7%>>$4<<,(%E/I*#$%&'(E/0 D,Q4,7%>>$4<<,(%E/I*$'(%<'55,<E/0 D,Q4,7%>>$4<<,(%E/I*+&<,$%'<=E/0 All other keys specified in a route can be accessed via Request::param(): 11!S<'!B&%8&(!#!$'(%<'55,<> T%8&7I*<,Q4,7%I*V#<#E"6,=W(#,"/0 11!U#(!?,!47,+!#(=B8,<,> D,Q4,7%>>$4<<,(%E/I*V#<#E"6,=W(#,"/0 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, #$%&'(, $'(%<'55,< and +&<,$%'<= are not accessible via Request::param(). For example, with the following route: D'4%,>>7,%E"#+7"."#+1F#+*E1F#NN&5&#%,*/"/ I*+,N#45%7E#<<#=E !!!!"$'(%<'55,<"!)*!"#+7". !!!!"#$%&'("!!!!!)*!"&(+,-". //0 If a url matches the route, then U'(%<'55,<WH+7>>&(+,-E/ will be called. You can access the parameters by using the V#<#E/ method of the controller's Request. Remember to define a default value (via the second, optional parameter of Request::param) if you didn't in I*+,N#45%7E/. $5#77!U'(%<'55,<WH+7!,-%,(+7!U'(%<'55,<!X !!!!"#$%&'!(#)'*&+)!,'*&+)-&)./012 !!!!3 !!!!!!!!4,.!5!4*6&789:/;#/7*89",:,<1=,.=2> !!!!!!!!4,((&%&,*/!5!4*6&789:/;#/7*89",:,<1=,((&%&,*/=?ABB2> !!!!C Where should routes be defined? The established convention is to either place your custom routes in the DEFGHIJKL<+.#%/9K&)&*M"6" file of your module if the routes belong to a module, or simply insert them into the HGGGHIJK$++*7*:,"M"6" 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: ANBOO7&*/1=,.<&)K/.&*K#7/:K=M4#7/:-&.2> 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 (//.7 route example from above: N+#*/OOP/*1=(//.7=289#:&1,::,Q1 !!=#7/:-&.=!59!4#7/:-&.? !!=,'*&+)=!59!='+<</)*7=? !!=(+:<,*=!59!=:77= 22> Let's say you decided later to make that route definition more verbose by changing it to (//.7KL#7/:-&.9 1KL,'*&+)92ML(+:<,*9. 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 1KL&.92 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 #7/:7K%&7*, we can do the following to generate uris in the format #7/:7KR&/SK4&.: 4*6&789:/;#/7*89#:&1,::,Q1=,'*&+)=!59!=R&/S=?!=&.=!59!4#7/:-&.22> Or if within a view, the preferable method is: N/;#/7*OO&)7*,)'/1289#:&1,::,Q1=,'*&+)=!59!=R&/S=?!=&.=!59!4#7/:-&.22> TODO: examples of using html::anchor in addition to the above examples Testing routes TODO: mention bluehawk's devtools module Tips and Common Mistakes This is a collection of tips and common mistakes or errors you may encounter. Never edit the !"!#$% 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 and transparent 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 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! Handling lots of routes Sometimes your application is sufficiently complex that you have many routes and it becomes unmanageable to put them all in bootstrap.php. If this is the case, simply make a !"#$%&'()( file in APPPATH and require that in your bootstrap: !%*#+!%,"-.%/0111023'4!"#$%&4'5627 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. 8%9:%.$+"-5;.%($+"-/</=>/?/A:B&&/."-$!"::%!,C&"D%$)+-EF/G"%&/-"$/%;+&$ HH/I)%!%/C&"D%$)+-EF/+&/(B!$/"9/$)%/#!:/J"#/%-$%!%G/+-/J"#!/K!"I&%! 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: 5!!"!5;.%($+"-/</L"$+.%/?/M-G%9+-%G/+-G%;/+G NOP1023H"!DH.:B&&%&HQ")B-BH"!D'()(/</>RST/? 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 for more details. Migrating from 3.1.x Config The configuration system has been rewritten to make it more flexible. The most significant change is that config groups are now merged across all readers, similar to how files cascade within the cascading filesystem. Therefore you can now override a single config value (perhaps a database connection string, or a 'send-from' email address) and store it in a separate config source (environment config file / database / custom) while inheriting the remaining settings from that group from your application's configuration files. In other respects, the majority of the public API should still operate in the same way, however the one major change is the transition from using U")B-B."-9+EVW to U")B-BX."-9+E=F:"BGVW, where U")B-BX."-9+E is an instance of A"-9+E. A"-9+E:"BGVW works almost identically to U")B-B."-9+EVW, e.g.: U")B-BX."-9+E=F:"BGV4G"$'-"$B$+"-4W U")B-BX."-9+E=F:"BGV4G"$4W=F-"$B$+"- A simple find/replace for !"#$%$&&'"%()*/!"#$%$&&+'"%()*,-."$/ within your project should fix this. The terminology for config sources has also changed. Pre 3.2 config was loaded from "Config Readers" which both read and wrote config. In 3.2 there are Config Readers and Config Writers, both of which are a type of Config Source. A Config Reader is implemented by implementing the !"#$%$01"%()*023$/34 interface; similarly a Config Writer is implemented by implementing the !"#$%$01"%()*054)634 interface. e.g. for Database: '.$778!"#$%$01"%()*09$6$:$73023$/348);<.3;3%678!"#$%$01"%()*023$/34 '.$778!"#$%$01"%()*09$6$:$73054)63483=63%/78!"#$%$01"%()*09$6$:$73023$/348);<.3;3%678!"#$%$01"%()*054 Although not enforced, the convention is that writers extend config readers. To help maintain backwards compatability when loading config sources empty classes are provided for the db/file sources which extends the source's reader/writer. e.g. '.$778!"#$%$01"%()*0>).383=63%/78!"#$%$01"%()*0>).3023$/34 '.$778!"#$%$01"%()*09$6$:$7383=63%/78!"#$%$01"%()*09$6$:$73054)634 External requests In Kohana 3.2, 23?37601.)3%60A=634%$. now has three separate drivers to handle external requests; 23?37601.)3%6014. is the default driver, using the PHP Curl extension 23?37601.)3%60BCCD uses the PECL HTTP extension 23?37601.)3%60E643$; uses streams native to PHP and requires no extensions. However this method is slower than the alternatives. Unless otherwise specified, 23?37601.)3%6014. will be used for all external requests. This can be changed for all external requests, or for individual requests. To set an external driver across all requests, add the following to the $<<.)'$6)"%F:""6764$<G<#< file; FF8E368$..83=634%$.843?376786"8738DA1H8BCCD 23?37601.)3%60A=634%$.&&+'.)3%68I8J23?37601.)3%60BCCDJK Alternatively it is possible to set a specific client to an individual Request. FF8E3686#38E643$;8'.)3%686"8$%8)%/)L)/$.843?3768$%/ FF8A=3'63 +437<"%738I823?376&&($'6"4MNJ#66<&FFO"#$%$(4$;3P"4OG"4*JQ 8888,-'.)3%6N%3P823?37601.)3%60E643$;Q 8888,-3=3'63NQK HTTP cache control Kohana 3.1 introduced HTTP cache control, providing RFC 2616 fully compliant transparent caching of responses. Kohana 3.2 builds on this moving all caching logic out of 23?37601.)3%6 into BCCD01$'#3. HTTP Cache requires the Cache module to be enabled in all versions of Kohana! In Kohana 3.1, HTTP caching was enabled doing the following; FF8R<<.M8'$'#386"8$843?376 +43?3768I823?376&&($'6"4MNJ(""F:$4JS81$'#3&&)%76$%'3NJ;3;'$'#3JQQK FF8T%8'"%64"..34S83%7438437<"%73873678'$'#38'"%64".S FF86#)78P)..8'$'#386#38437<"%738("48"%38#"4 +6#)7,-437<"%73,-#3$/347NJ'$'#3,'"%64".JS8 8888J<:.)'S8;$=,$*3IUVWWJQK In Kohana 3.2, HTTP caching is enabled slightly differently; !!"#$$%&"'(')*"+,"-*./*0+ 1-*./*0+"2"3*./*0+445('+,-&675,,!8(-79 """":;;<=>(')*445('+,-&67?*?'(')*7A !!"BC"',C+-,%%*-9"*C0/-*"-*0$,C0*"0*+0"'(')*"',C+-,%9 !!"+)D0"ED%%"'(')*"+)*"-*0$,C0*"5,-",C*"),/- 1+)D0FG-*0$,C0*FG)*(H*-067'(')*F',C+-,%79" """"7$/8%D'9"?(IF(J*2KLMM7A Controller Action Parameters In 3.1, controller action parameters were deprecated. In 3.2, these behavior has been removed. If you had any code like: $/8%D'"5/C'+D,C"('+D,C=DCH*I61DH N """"!!"OOO"',H* P You'll need to change it to: $/8%D'"5/C'+D,C"('+D,C=DCH*I6 N """"1DH"2"1+)D0FG-*./*0+FG$(-(?67DH7A """"!!"OOO"',H* P Form Class If you used Form::open(), the default behavior has changed. It used to default to "/" (your home page), but now an empty parameter will default to the current URI. 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 or print_r, but using HTML for extra formatting. !!"QD0$%(&"("H/?$",5"+)*"15,,"(CH"18(-"R(-D(8%*0 *'),"Q*8/J44R(-0615,,9"18(-A Kohana also provides a method to show the source code of a particular file using Debug::source. !!"QD0$%(&"+)D0"%DC*",5"0,/-'*"',H* *'),"Q*8/J440,/-'*6==SBTU==9"==TBVU==A If you want to display information about your application files without exposing the installation directory, you can use Debug::path: !!"QD0$%(&0"W#<<<#;:!'(')*W"-(+)*-"+)(C"+)*"-*(%"$(+) *'),"Q*8/J44$(+)6#<<<#;:O7'(')*7A 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. Loading Classes Kohana takes advantage of PHP autoloading. This removes the need to call include or 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: >,,XD*440*+67?&',,XD*79"7(C&"0+-DCJ"R(%/*7A Or to load an Encrypt instance, just call Encrypt::instance: !"#$%&'()*)+#$%&'(,,-#.(/#$"012 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 $3/..".4 directory of the filesystem 2. Any underscore characters in the class name are converted to slashes 3. The filename is lowercase When calling a class that has not been loaded (eg: 5"..-6#78669-"), Kohana will search the filesystem using Kohana::find_file for a file named $3/..".4."..-6#4$669-":';'. 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. Custom Autoloaders Kohana's default autoloader is enabled in /''3-$/(-6#4<66(.(%/':';' using spl_autoload_register: .'37/=(636/>7%"?-.("%0/%%/&0A6;/#/B)/=(6736/>112 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. Create a C"#>6% directory at /''3-$/(-6#4C"#>6%. This keeps third party software separate from your application classes. Move the decompressed Zend folder containing Zend Framework to /''3-$/(-6#4C"#>6%4D"#>. Include Zend's Autoloader in your bootstrap Somewhere in /''3-$/(-6#4<66(.(%/':';', copy the following code: 4EE )E)+#/<3")D"#>)F%/G"H6%9)/=(636/>-#? )E4 -I)0!'/(;)*)A6;/#/,,I-#>7I-3"0C"#>6%B)D"#>4J6/>"%11 K ))))-#-7."(0-#$3=>"7'/(;B ))))-#-7?"(0-#$3=>"7'/(;1:LMNO75+LMPMNQP:>-%#/G"0>-%#/G"0!'/(;1112 ))))%"R=-%"76#$")D"#>4J6/>"%4M=(636/>"%:';'2 ))))D"#>7J6/>"%7M=(636/>"%,,?"(S#.(/#$"012 T Usage example You can now autoload any Zend Framework classes from inside your Kohana application. -I)0!C/3->/("0!7LQ5N11 K ))))!G/-3"%)*)#"H)D"#>7U/-32 ))))!G/-3"%VW."(X6>&O(G30!C-"H1 ))))))))VW."(F%6G0A6;/#/,,!$6#I-?VW36/>0.-("1VW"G/-37I%6G1 ))))))))VW/>>N60!"G/-31 ))))))))VW."(5=<Y"$(0!G"../?"1 !!!!!!!!"#$%&'()* + Transparent Class Extension The cascading filesystem allows transparent class extension. For instance, the class Cookie is defined in ,-,./012345$$%$236678%9:;: as: 345$$!<6678%!%=>%&'$!?6;5&5<6678%!A+ 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 /.../012345$$%$236678%9:;: 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 5::4835>86&2345$$%$236678%9:;: that extends Kohana_Cookie, and adds your functions: BC:;:!'%D8&%'(E,-,./01E)!6F!'8%(EG6!'8F%3>!$3F8:>!533%$$9E)* 345$$!<6678%!%=>%&'$!?6;5&5<6678%!A !!!!2HH !!!!!H!IJ5F!!K8=%'!!'%D5L4>!%&3FM:>86&!8&$>5&3% !!!!!H2 !!!!:LN483!$>5>83!O%&3FM:>86&!P!E'%D5L4>E* !!!!2HH !!!!!H!,%>$!5&!%&3FM:>%'!36678%9 !!!!!H !!!!!H!IL$%$!!<6678%QQ$%> !!!!!H!IL$%$!!R&3FM:>QQ%&36'% !!!!!H2 !!!!!:LN483!$>5>83!DL&3>86&!%&3FM:>(O&5K%S!OJ54L%S!O%=:8F5>86&!P!GTUU) !!!!!A !!!!!!!!!OJ54L%!P!R&3FM:>QQ8&$>5&3%(<6678%QQO%&3F:M>86&)"#%&36'%(($>F8&V)!OJ54L%)* !!!!!!!!!:5F%&>QQ$%>(O&5K%S!OJ54L%S!O%=:8F5>86&)* !!!!!+ !!!!!2HH !!!!!!H!W%>$!5&!%&3FM:>%'!36678%9 !!!!!!H !!!!!!H!IL$%$!!<6678%QQV%> !!!!!!H!IL$%$!!R&3FM:>QQ'%36'% !!!!!!H2 !!!!!!:LN483!$>5>83!DL&3>86&!'%3FM:>(O&5K%S!O'%D5L4>!P!GTUU) !!!!!!A !!!!!!!!!!8D!(OJ54L%!P!:5F%&>QQV%>(O&5K%S!GTUU)) !!!!!!!!!!A !!!!!!!!!!!!!!OJ54L%!P!R&3FM:>QQ8&$>5&3%(<6678%QQO%&3FM:>86&)"#'%36'%(OJ54L%)* !!!!!!!!!!+ !!!!!!!!!!F%>LF&!8$$%>(OJ54L%)!C!OJ54L%!Q!O'%D5L4>* !!!!!!+ +!22!R&'!<6678% Now calling <6678%QQ%&3FM:>(E$%3F%>ES!O'5>5) will create an encrypted cookie which we can decrypt with O'5>5 P!<6678%QQ'%3FM:>(E$%3F%>E). 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 !"#$$%$&!''()%*+,+ in the cascading filesystem. It looks in #++")!#-)'., then each module, then $/$-%0. The file is found in $/$-%0 and is included. Of coures, $/$-%0&!"#$$%$&!''()%*+,+ is just an empty class which extends 1',#.#23''()%. Again, Kohana::autoload is called this time looking for !"#$$%$&(',#.#&!''()%*+,+ which it finds in $/$-%0. When you add your transparently extended cookie class at #++")!#-)'.&!"#$$%$&!''()%*+,+ this file essentially "replaces" the file at $/$-%0&!"#$$%$&!''()%*+,+ without actually touching it. This happens because this time when we use the Cookie class Kohana::autoload looks for !"#$$%$&!''()%*+,+ and finds the file in #++")!#-)'. and includes that one, instead of the one in system. Example: changing Cookie settings If you are using the Cookie 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 #++")!#-)'.&!"#$$%$&!''()%*+,+ or a module (456789:&;0'<="%.#0%>&!"#$$%$&!''()%*+,+). !"#$$?3''()%?%-%.<$?1',#.#23''()%?A ????&&?B%-?#?.%C?$#"- ????+=D")!?E$#"-?F?G$'0%?.%C?D%--%H?H#.<'0?$#"-?+,H#$%GI ????&&?6'.J-?#""'C?K#L#$!H)+-?#!!%$$?-'?!''()%$ ????+=D")!?E,--+'."/?F?9MNOI P 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 456789:&0/0'<&%.!H/+-%<&!''()%*+,+: !"#$$?O.!H/+-%<23''()%?%-%.<$?1',#.#23''()%?A ????&&?N$%?-,%?$#0%?%.!H/+-QR?#.<?<%!H/+-QR?0%-,'<$?#$?#D'L% P And create 456789:&0/0'<&!''()%*+,+: !"#$$?3''()%?%-%.<$?O.!H/+-%<23''()%?AP 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 O.!H/+-%<23''()% (rather than 1',#.#23''()%) in their application folder. 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 !"#$$%$ 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 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 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 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 page. Remote - Remote server access helper using 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 page. Session - Covered in more detail on the 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. Requests Kohana includes a flexible HMVC request system. It supports out of the box support for internal requests and external requests. HMVC stands for &'%(#(!)'!#"*+,-%"*.'%/*0,12(,""%( 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 to direct the application based on the URI that is passed to it. A basic internal request might look something like: !"#$%#&'()(*#$%#&'++,-.'/"0123#4./5#267 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 89:#;<=>=) is called the "initial request". You can access this request via: *#$%#&'++898'8-4167 You should only use this method if you are absolutely sure you want the initial request. Otherwise you should use the *#$%#&'++.%""#9'16 method. Sub-requests You can call a request at any time in your application by using the *#$%#&'++,-.'/"016 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: !&%?"#$%#&'()(A(!'>8&BC"#$%#&'BC8&898'8-416 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: DD(E>8&(%&#&(FGE !"#$%#&'()(*#$%#&'++,-.'/"012>''=+DD333<H//H4#<./5D267 DD(E>8&(%&#&(IJE !"#$%#&'()(*#$%#&'++,-.'/"012>''=+DD#;-5=4#<./5D=%'-=826BC5#'>/:1*#$%#&'++IJE6BC?/:01K&/9#9./:#12'> DD(E>8&(%&#&(INOE !"#$%#&'()(*#$%#&'++,-.'/"012>''=+DD#;-5=4#<./5D=/&'-=826BC5#'>/:1*#$%#&'++INOE6BC=/&'1-""-012,//2() Executing Requests To execute a request, use the #;#.%'#16 method on it. This will give you a response object. !"#$%#&'()(*#$%#&'++,-.'/"0123#4./5#267 !"#&=/9&#()(!"#$%#&'BC#;#.%'#167 Request Cache Control You can cache requests for fast execution by passing a cache instance in as the second parameter of factory: !"#$%#&'()(*#$%#&'++,-.'/"0123#4./5#2M(L-.>#++89&'-9.#1667 TODO 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. 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: !!"#$%"%&$"'$''()*"(*'%+*,$ -'$''()*"."/$''()*00(*'%+*,$123 When using sessions, you can also get all of the current session data using the Session::as_array method: !!"#$%"+44")5"%&$"'$''()*"6+%+"+'"+*"+77+8 -6+%+"."-'$''()*9:+';+77+8123 You can also use this to overload the -;/<//=>? global to get and set data in a way more similar to standard PHP: !!">$74)+6"-;/<//=>?"A(%&"%&$"'$''()*"6+%+ -;/<//=>?".B"-'$''()*9:+';+77+8123 !!"/$%"'$''()*"6+%+ -;/<//=>?C-D$8E"."-+4F$3 Storing Data Storing session or cookie data is done using the '$% method: !!"/$%"'$''()*"6+%+ -'$''()*9:'$%1-D$8G"-+4F$23 !!">7 /$''()*00(*'%+*,$129:'$%1-D$8G"-+4F$23 !!"/%)7$"+"F'$7"(6 -'$''()*9:'$%1HF'$7;(6HG"IJ23 Retrieving Data Getting session or cookie data is done using the K$% method: !!"#$%"'$''()*"6+%+ -6+%+"."-'$''()*9:K$%1-D$8G"-6$5+F4%;+4F$23 !!"#$%"%&$"F'$7"(6 -F'$7"."-'$''()*9:K$%1HF'$7;(6H23 Deleting Data Deleting session or cookie data is done using the 6$4$%$ method: !!"L$4$%$"'$''()*"6+%+ -'$''()*9:6$4$%$1-D$823 !!"L$4$%$"%&$"F'$7"(6 -'$''()*9:6$4$%$1HF'$7;(6H23 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 in !"!#$%$ or defined by 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 $%&'(%)*+,, for example: -*&&$.%//$%&'(%)*+0)..1$*0, Session Adapter Settings You can apply configuration settings to each of the session adapters by creating a session config file at 23332456).%7$86&*&&$.%#!"!. 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. 9*':9%;(99(<+ ;;;;0%('$=*0;>?;(99(<+ ;;;;;;;;0%(*0;>?;0&*&&$.%A%(*0B ;;;;;;;;0C$7*'$*0;>?;DEFGGB ;;;;,B ;;;;0)..1$*0;>?;(99(<+ ;;;;;;;;0%(*0;>?;0)..1$*A%(*0B ;;;;;;;;0*%)9<!'*H0;>?;4IJKB ;;;;;;;;0C$7*'$*0;>?;DEFGGB ;;;;,B ;;;;0H('(L(&*0;>?;(99(<+ ;;;;;;;;0%(*0;>?;0)..1$*A%(*0B ;;;;;;;;0*%)9<!'*H0;>?;4IJKB ;;;;;;;;0C$7*'$*0;>?;DEFGGB ;;;;;;;;089.:!0;>?;0H*7(:C'0B ;;;;;;;;0'(LC*0;>?;0'(LC*A%(*0B ;;;;;;;;0).C:%&0;>?;(99(<+ ;;;;;;;;;;;;0&*&&$.%A$H0;;>?;0&*&&$.%A$H0B ;;;;;;;;;;;;0C(&'A()'$=*0;>?;0C(&'A()'$=*0B ;;;;;;;;;;;;0).%'*%'&0;;;;>?;0).%'*%'&0 ;;;;;;;;,B ;;;;;;;;08)0;>?;MGGB ;;;;,B ,N Native Adapter Type Setting Description Default &'9$%8 name name of the session O&*&&$.%O $%'*8*9 lifetime number of seconds the session should live for G Cookie Adapter Type Setting Description Default &'9$%8 name name of the cookie used to store the session data O&*&&$.%O L..C*(% encrypted encrypt the session data using Encrypt? P2Q-K !"#$%$& lifetime number of seconds the session should live for ' Database Adapter Type Setting Description Default (#&!"% group Database::instance group name )*$+,-.#) (#&!"% table table name to store sessions in )($((!/"() ,&&,0 columns associative array of column aliases ,&&,0 !"#$%$& gc 1:x chance that garbage collection will be run 1'' (#&!"% name name of the cookie used to store the session data )($((!/") 2//.$," encrypted encrypt the session data using Encrypt? 34567 !"#$%$& lifetime number of seconds the session should live for ' Table Schema You will need to create the session storage table in the database. This is the default schema: 8974:7;:4<57;;=($((!/"(=;> ;;;;=($((!/"?!*=;498A49>BCD;EF:;EG55H ;;;;=.,(#?,I#!J$=;KE:;GE6KLE7M;EF:;EG55H ;;;;=I/"#$"#(=;:7N:;EF:;EG55H ;;;;O9KP49Q;R7Q;>=($((!/"?!*=DH ;;;;KEM7N;>=.,(#?,I#!J$=D D;7ELKE7;S;PQK64PT 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 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 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: 8//U!$VVW(,.#;S;X+//2,&XT Or define an extended cookie class in your application: I.,((;8//U!$;$Y#$"*(;R/Z,",?8//U!$ [ ;;;;\-2.!I;(#,#!I;W(,.#;S;X+//2,&XT ] You should set the salt to a secure value. The example above is only for demonstrative purposes. Nothing stops you from using W?8FFRK7 like normal, but you can not mix using the Cookie class and the regular W?8FFRK7 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: !!"#$%"&''()$"*+%+ ,''()$--.$%/0($12"03+45$67 !!"#%'8$"+"5.$8")* ,''()$--.$%/95.$8:)*92";<67 Retrieving Data Getting session or cookie data is done using the Cookie::get method: !!"=$%"&''()$"*+%+ 0*+%+">",''()$--?$%/0($12"0*$+54%:3+45$67 !!"=$%"%A$"5.$8")* 05.$8">",''()$--?$%/95.$8:)*967 Deleting Data Deleting session or cookie data is done using the Cookie::delete method: !!"B$4$%$"&''()$"*+%+ ,''()$--*$4$%$/0($167 !!"B$4$%$"%A$"5.$8")* ,''()$--*$4$%$/95.$8:)*967 Cookie Settings All of the cookie settings are changed using static properties. You can either change these settings in C''%.%8+DEDAD or by using transparent 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: ,''()$--0.+4%">"91'58".$&8$%").".+$"F)%A"G$97 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: !!"#$%"&''()$."%'"$HD)8$"+%$8";"F$$( ,''()$--0$HD)8+%)'I">"J<KL<<7 !!"M4%$8I+%)3$"%'"5.)I?"8+F")I%$?$8.2"'8"C$%%$8"&4+8)%1 ,''()$--0$HD)8+%)'I">"B+%$--NOOP7 The path that the cookie can be accessed from can be restricted using the Cookie::$path setting. !!"M44'F"&''()$."'I41"FA$I"?')I?"%'"!D5C4)&!Q ,''()$--0D+%A">"9!D5C4)&!97 The domain that the cookie can be accessed from can also be restricted, using the Cookie::$domain setting. !!"M44'F"&''()$."'I41"'I"%A$"*'G+)I"FFFE$H+GD4$E&'G ,''()$--0*'G+)I">"9FFFE$H+GD4$E&'G97 If you want to make the cookie accessible on all subdomains, use a dot at the beginning of the domain. !!"#$$%&"'%%()*+",%"-*".''*++*/"%0"*1.23$*4'%2".0/"54*1.23$*4'%2 6%%()*778/%2.)0"9":4*1.23$*4'%2:; To only allow the cookie to be accessed over a secure (HTTPS) connection, use the Cookie::$secure setting. !!"#$$%&"'%%()*+",%"-*".''*++*/"%0$<"%0"."+*'=>*"'%00*',)%0 6%%()*778+*'=>*"9"?AB; !!"#$$%&"'%%()*+",%"-*".''*++*/"%0".0<"'%00*',)%0 6%%()*778+*'=>*"9"C#DEB; To prevent cookies from being accessed using Javascript, you can change the Cookie::$httponly setting. !!"F.(*"'%%()*+")0.''*++)-$*",%"G.H.+'>)3, 6%%()*778I,,3%0$<"9"?AB; 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. Fragments use Kohana::cache() and will be placed in the cache directory (.33$)'.,)%0!'.'I* 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 )J statement at the beginning of what you want cached, and Fragment::save() at the end. They use output buffering 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 ,>=* as the third parameter to Fragment::load(); You can force the deletion of a Fragment using Fragment::delete(), or specify a lifetime of 0. !!"6.'I*"J%>"K"2)0=,*+L".0/"'.'I*"*.'I"$.0M=.M* )J"N"O"C>.M2*0,77$%./N:J%%-.>:L"P.,*77FQRA?B"5"KL",>=*SS T """"!!"#0<,I)0M",I.,")+"*'I%:*/"I*>*"&)$$"-*"+.H*/ """"C>.M2*0,77+.H*NS; U 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. )J"N"O"C>.M2*0,77$%./N:3)VWWW:L"P.,*77XYA"5"ZSS T""" """"!!"6I.0M*"J=0',)%0"0*+,)0M"$)2), """")0)[+*,N:1/*-=M42.1[0*+,)0M[$*H*$:LVWWWS; """"!!"E%=>'*7"I,,37!!2M''$4'%2!\WW]!WV!\\!3I3^'.$'=$.,*^3)^>*H)+),*/ """"J=0',)%0"-'J.',N80S """"T """""">*,=>0"N80"99"W"__"8099"VS"`"V"7"-'2=$N80L-'J.',N80^VSS; """"U !!!!"#$%&'($!)%*'+,*-.%'/'($0 !!!!1 !!!!!!!!,$#2!3!45,6!3!45 !!!!!!!!)%/%78.+,*-.%'/'($9:05 !!!!!!!!,8'2'&!3!+,*-.%'/'($9:0;<=5 !!!!!!!!>?'8.+,6!!,8'2'&0 !!!!!!!!1 !!!!!!!!!!!!,$#2!3!)%7AA+,$#2B!)%A'C+)%2#8+)%7AA+D<:EF<=4FDB)%2#8+DE=E<=4<:=DB!,600B)%2#8+)%*(>+G<B!, !!!!!!!!!!!!99,65 !!!!!!!!L !!!!!!!!-.&#-$!)%A'C+<B+)%2#8+<JB+,$#2000B,*-.%'/'($05 !!!!L !!!!.%?(!)%*'+<44405 !!!!M-7N2.$&OO/7C.+05 L .%?(!P'.>OO"7%&(-Q+D*-("'8.-;/&7&/D05 RS 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, then use Fragment to cache the results. ,"..A!3!T?&&*O;;.$U>'6'*.A'7U(-N;>;'$A.VU*?*R&'&8.3W*.%'78OX.%.$&Y?7$N./Z"..A3-//T5 ,8'2'&!3!E45 ;;!['/*87Q.A!"..A/!7-.!%7%?.A!"(-!:4!/.%($A/!+A."7#8&0 '"!+!\!M-7N2.$&OO8(7A+D-//ODU,"..A00O !!!!;;!]7-/.!&?.!"..A !!!!,'&.2/!3!M..AOO*7-/.+,"..AB!,8'2'&05 !!!!"(-.7%?!+,'&.2/!7/!,'&.20O !!!!!!!!;;!Y($C.-&!,'&.2!&(!()^.%& !!!!!!!!,'&.2!3!+()^.%&0!,'&.25 !!!!!!!!.%?(!_`abOO7$%?(-+,'&.2GS8'$6B,'&.2GS&'&8.05 !!!!!!!!RS !!!!!!!!)8(%6K#(&.S !!!!!!!!!!!!*S7#&?(-O!R*?*!.%?(!,'&.2GS%-.7&(-!RS;*S !!!!!!!!!!!!*SA7&.O!R*?*!.%?(!,'&.2GS*#)[7&.!RS;*S !!!!!!!!;)8(%6K#(&.S !!!!!!!!R*?* !!!!.$A"(-.7%?5 !!!!M-7N2.$&OO/7C.+05 .$A'"5 .%?(!P'.>OO"7%&(-Q+D*-("'8.-;/&7&/D05 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. !!"#$%&'"&()'*$+'",(-",./'").012'3 .,"4"5"6-$+)'02778($94:&()'*$+':;"<$2'77=>?AB"C"DEE7 """"'%&("FG*HI()'"*$+'"321,,G!*HFJ """"!!"K-'2'09"8.L'"M'"$-'"$%21$88N"9(.0+"3()'2&.0+"7E """"38''*4OEJ """"!!"#$%&'"2&.3"'/'-N"&(1-"3.0%'".2"9('30:2"%&$0+'"$3"(,2'0 """".,"4"5"6-$+)'02778($94:&()'*$+'P31Q,-$+)'02:;"<$2'77IRSEE7 """"""""'%&("FG*HI()'"*$+'"3*'%.$8"2&.0+NG!*HFJ """"""""!!"K-'2'09"8.L'"2&.3"2$L'3"$"8(0+"2.)' """"""""38''*4DEJ """"6-$+)'02773$/'4EJ"'09.,J """"'%&("FG*H=(-'"&()'"*$+'"321,,G!*HFJ """"6-$+)'02773$/'4EJ '09.,J '%&("T.'M77,$%2(-N4:*-(,.8'-!32$23:EJ 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 *-(,.8' setting must be ASB 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. *1Q8.%",10%2.(0",((Q$-4U.0*12E V """"!!"W'"31-'"2("(08N"*-(,.8'".,".2:3"'0$Q8'9 """".,"4X(&$0$77U*-(,.8.0+"YYY"ASBE """"V """"""""!!"Z2$-2"$"0'M"Q'0%&)$-L """"""""UQ'0%&)$-L"Y"K-(,.8'-7732$-24:[(1-"#$2'+(-N:;"\\6?#A>R?\\EJ """"] """"!!"<("3()'"321,, """".,"4.33'24UQ'0%&)$-LEE """"V """"""""!!"Z2(*"2&'"Q'0%&)$-L """"""""K-(,.8'-7732(*4UQ'0%&)$-LEJ """"] """"-'21-0"U3()'2&.0+J ] 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: !"#$#%&'$(%)*&+,,-.'/(0123#0(-*4&056/./637%"8 Preview (This is the actual profiler stats for this page.) Requests 0.020559 s 1,654.2813 kB BENCHMARK MIN MAX AVERAGE TOTAL "guide/kohana/profiling" (1) Kohana 0.002239 s 15.6172 kB BENCHMARK MIN MAX AVERAGE TOTAL find_file (28) Application Execution (341) 0.032835 s 0.293552 s 0.048119 s 0.044976 s 2,998.4453 kB 3,000.7656 kB 3,000.2100 kB 2,999.0078 kB 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. 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 Validation object by the array key, called a "field name". labels A label is a human-readable version of the field name. rules A rule is a callback used to decide whether or not to add an error to a field Note that any valid PHP callback can be used as a rule. Using 9:;< 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: =#(6/%>%).4*?./*(,,-.'/(012=ABCD97E 0.020784 s 0.020784 s 0.020784 s 0.020784 s 1,661.9531 kB 1,661.9531 kB 1,661.9531 kB 1,661.9531 kB 0.000057 s 0.000223 s 0.000080 s 0.002239 s 0.5234 kB 0.6406 kB 0.5578 kB 15.6172 kB The !"#$% 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 Valid::email_domain Check that the domain of the email exists Valid::url Value must be a URL Valid::ip Value must be an IP address Valid::phone Value must be a phone number 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 syntax), and an array of parameters: !#&'()%*+,-.(/!01(.234!)5..&5)6345,,57/!"5,58(%(,934!"5,58(%(,:;;< If no parameters are specified, the field value will be passed to the callback. The following two rules are equivalent: !#&'()%*+,-.(/!01(.234=>#%?(8"%7=;< !#&'()%*+,-.(/!01(.234=>#%?(8"%7=345,,57/=A5.-(=;;< Rules defined in the Valid class can be added by using the method name alone. The following three rules are equivalent: !#&'()%*+,-.(/=>-8&(,=34="B#>(=;< !#&'()%*+,-.(/=>-8&(,=345,,57/=C5.12=34="B#>(=;;< !#&'()%*+,-.(/=>-8&(,=34=C5.12"B#>(=;< 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. !#&'()%*+&1>2/=8#2(.=34!-$(,?8#2(.;< DD4E-%-,(4)#2(4F1..4&(45&.(4%#4-$(48#2(.4%#4,(0(,(>)(4%B(4#&'()% !#&'()%*+,-.(/=-$(,>58(=34=$#8(?,-.(=345,,57/=8#2(.=;;< By default, the validation object will automatically bind the following values for you to use as rule parameters: !"#$%&#'%() - references the validation object !*%+$& - references the field name the rule is for !"#$,+ - 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 -./01. This allows many built in PHP functions to be used as rules, like %)2#33#4. 5(67+8'9:3,$+;<8($(3<=><%)2#33#4<=>#33#4;<!"#$,+<=>#33#4;<3+&<=><?3++)<=><6$,+<A Rules added to empty fields will run, but returning -./01 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. 5(67+8'9:3,$+;5*%+$&=><'B+23,$+<=>#33#4;<!"#$%&#'%()<=><!*%+$&<A C,6$%8>*,)8'%()>'B+23,$+;5"#$%&#'%()=>5*%+$& D >>>>%*>;E(F+'B%)?>G+)'>G3()? >>>>D >>>>>>>>5"#$%&#'%()9:+33(3;5*%+$&=><'B+23,$+<A >>>>H H )('2+FC'4 and F#'8B+E are the only rules that will run on empty fields and add errors by returning -./01. Example To start our example, we will perform validation on a 52IJ0K array that contains user registration information: 5C(E'>L>M#$%&#'%()!!*#8'(34;52IJ0KA Next we need to process the POST'ed information using Validation. To start, we need to add some rules: 5C(E' >>>>9:3,$+;<,E+3)#F+<=><)('2+FC'4< >>>>9:3,$+;<,E+3)#F+<=><3+?+N<=>#33#4;<!"#$,+<=><OPQ#9R2STUU5O%V< >>>>9:3,$+;<C#EEG(3&<=><)('2+FC'4< >>>>9:3,$+;<C#EEG(3&<=><F%)2$+)?'B<=>#33#4;<!"#$,+<=><W< >>>>9:3,$+;<8()*%3F<=>><F#'8B+E<=>#33#4;<!"#$%&#'%()<=><8()*%3F<=><C#EEG(3&< >>>>9:3,$+;<,E+2EE$<=><)('2+FC'4<A 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: 5C(E'9:3,$+;<,E+2EE$<=><%)2#33#4<=>#33#4;<!"#$,+<=>#33#4;<4+E<=><)(<A Note that all array parameters must still be wrapped in an array! Without the wrapping array, %)2#33#4 would be called as %)2#33#4;5"#$,+=><4+E<=><)(<, 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]: 5C(E'9:3,$+;<,E+3)#F+<=><XE+32Y(&+$!!,)%Z,+2,E+3)#F+<A The method XE+32Y(&+$!!,)%Z,+2,E+3)#F+; would be defined similar to: C,6$%8>E'#'%8>*,)8'%()>,)%Z,+2,E+3)#F+;5,E+3)#F+ D >>>>OO>[B+8\>%*>'B+>,E+3)#F+>#$3+#&4>+N%E'E>%)>'B+>&#'#6#E+ >>>>3+',3)>]>V^!!E+$+8';#33#4;V^!!+NC3;<[JX_K;,E+3)#F+<=><'('#$< >>>>>>>>9:*3(F;<,E+3E< >>>>>>>>9:GB+3+;<,E+3)#F+<=><L<=>5,E+3)#F+ >>>>>>>>9:+N+8,'+; >>>>>>>>9:?+';<'('#$<A ! 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 "##$%&"'%()*+%,-.*/.,0*0,1%.',02#3#: 45#3#6,&3(67(0899(#,):;65< 45#3#6%=6:>,00(0.;965< 4#6&$"..?8,.."1,<A(8,6,00(0.6-,0,6,)&(/)',0,BC6#$,".,6&3,&D6'3,6B,'"%$.6E(/6,)',0,B24*#< 4/$6&$"..?,00(0.< 45#3#6=(0,"&36:>,00(0.6".6>8,.."1,;965< 66664$%<45#3#6,&3(6>8,.."1,65<4*$%< 45#3#6,)B=(0,"&365< 45#3#6,)B%=65< 4B$< 66664B'<45#3#6,&3(67(0899$"F,$:G/.,0)"8,GC6GH.,0)"8,G;65<4*B'< 66664BB<45#3#6,&3(67(0899%)#/':G/.,0)"8,GC6>#(.'IG/.,0)"8,GJ;65<4*BB< 66664B'<45#3#6,&3(67(0899$"F,$:G#"..-(0BGC6GK"..-(0BG;65<4*B'< 66664BB<45#3#6,&3(67(0899#"..-(0B:G#"..-(0BG;65<4*BB< 66664BB6&$"..?3,$#<K"..-(0B.68/.'6F,6"'6$,".'6L6&3"0"&',0.6$()124*BB< 66664B'<45#3#6,&3(67(0899$"F,$:G&()=%08GC6GM()=%086K"..-(0BG;65<4*B'< 66664BB<45#3#6,&3(67(0899#"..-(0B:G&()=%08G;65<4*BB< 66664B'<45#3#6,&3(67(0899$"F,$:G/.,N..$GC6GH.,6,O'0"6.,&/0%'E5G;65<4*B'< 66664BB<45#3#6,&3(67(0899.,$,&':G/.,N..$GC6"00"E:GE,.G6?<6GP$-"E.GC6G)(G6?<6GQ)$E6-3,)6),&,.."0EG;C6> 66664BB6&$"..?3,$#<7(06.,&/0%'EC6AAR6%.6"$-"E.6/.,B6-3,)68"D%)16#"E8,)'.24*BB< 4*B$< 45#3#6,&3(67(0899./F8%':SHRRC6GA%1)6H#G;65< 45#3#6,&3(67(0899&$(.,:;65< 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 "##$%&"'%()*&$"..,.*&()'0($$,0*/.,02#3#: &$"..6M()'0($$,0NH.,06,O',)B.6M()'0($$,06T 6666#/F$%&6=/)&'%()6"&'%()N0,1%.',0:; 6666T 66666666>/.,06?6U(B,$99="&'(0E:G/.,0G;V 66666666>#(.'6?6W"$%B"'%()99="&'(0E:>NKQAX; 666666666666Y<0/$,:G/.,0)"8,GC6G)('N,8#'EG; 666666666666Y<0/$,:G/.,0)"8,GC6G0,1,OGC6"00"E:G9+"$/,GC6G*ZI"Y[N2J\\>*%]G;; 666666666666Y<0/$,:G/.,0)"8,GC6"00"E:>/.,0C6G/)%^/,N/.,0)"8,G;; 666666666666Y<0/$,:G#"..-(0BGC6G)('N,8#'EG; 666666666666Y<0/$,:G#"..-(0BGC6G8%)N$,)1'3GC6"00"E:G9+"$/,GC6L;; 666666666666Y<0/$,:G&()=%08GC66G8"'&3,.GC6"00"E:G9+"$%B"'%()GC6G9=%,$BGC6G#"..-(0BG;; 666666666666Y<0/$,:G/.,N..$GC6G)('N,8#'EG; 666666666666Y<0/$,:G/.,N..$GC6G%)N"00"EGC6"00"E:G9+"$/,GC6"00"E:GE,.GC6G)(G;;;V 66666666%=6:>#(.'Y<&3,&D:;; 66666666T !!!!!!!!!!!!""!#$%$!&$'!())*!+$,-.$%)./!0)1-'%)0!%&)!2')0 !!!!!!!!!!!!32')0450)1-'%)06378'%9: !!!!!!!!!!!!""!;,<$='!0).-0)>%!$?%)0!$!'2>>)''?2,!ABC!%8!70)+)*%!0)?0)'&!<$0*-*1' !!!!!!!!!!!!3%&-'450)D2)'%450).-0)>%6E2')0"708?-,)E9: !!!!!!!!F !!!!!!!!""!G$,-.$%-8*!?$-,)./!>8,,)>%!%&)!)0080' !!!!!!!!3)0080'!H!378'%45)0080'6E2')0E9: !!!!!!!!""!#-'7,$=!%&)!0)1-'%0$%-8*!?80I !!!!!!!!3%&-'450)'78*')45(8.=6G-)<JJ?$>%80=6E2')0"0)1-'%)0E99 !!!!!!!!!!!!45(-*.6E78'%E/!378'%9 !!!!!!!!!!!!45(-*.6E)0080'E/!3)0080'9: !!!!F F We will also need a user model, which will be placed in $77,->$%-8*">,$'')'"I8.),"2')0K7&7: >,$''!L8.),MN')0!)O%)*.'!L8.),!P !!!!72(,->!?2*>%-8*!0)1-'%)063$00$=9 !!!!P !!!!!!!!""!Q0)$%)!$!*)<!2')0!0)>80.!-*!%&)!.$%$($') !!!!!!!!3-.!H!#RJJ-*')0%6$00$=MS)='63$00$=99 !!!!!!!!!!!!45+$,2)'63$00$=9 !!!!!!!!!!!!45)O)>2%)69: !!!!!!!!""!B$+)!%&)!*)<!2')0!-.!%8!$!>88S-) !!!!!!!!>88S-)JJ')%6E2')0E/!3-.9: !!!!!!!!0)%20*!3-.: !!!!F F That is it, we have a complete user registration example that properly checks user input! 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 Discuss database security. How to avoid injection, etc. Not sure why I'm linking to this: http://kohanaframework.org/guide/security.database Discuss using encryption, including setting the encryption key in config. 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 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. !"" #"#$%&#&'%#%()*+,(-%(&#.&+*(/#01#&'%#2,-3*(#42%5367&.#&,#8,'3(399:;<;=>?;ABCD #"! 8,'3(399E%()*+,(-%(&#F#4EG$;H<;HIJ$;H<;HGAK;JL#MFF#J7,N37',.&JC#O#8,'3(399?H>:PQBR>A#9#8,'3(399 !"" #"#R(*&*37*.%#8,'3(3#03.%2#,(#%()*+,(-%(& #"! 8,'3(399*(*&43++314 ####J03.%G6+7J###FT#J!JU ####J*(2%VG5*7%J#FT#WK=$;U ####JX+,5*7%J####FT#8,'3(399E%()*+,(-%(&#MFF#8,'3(399?H>:PQBR>AU ####JN3N'*(/J####FT#8,'3(399E%()*+,(-%(&#FFF#8,'3(399?H>:PQBR>AU CCS *(2%VDX'X: ####!"" #####"#;V%N6&%#&'%#-3*(#+%Y6%.&#6.*(/#?KBZGRAW>D#R5#(,#PHR#.,6+N%#*.#.X%N*5*%2U #####"#&'%#PHR#[*77#0%#36&,-3&*N3771#2%&%N&%2D #####"! ####E+%Y6%.&#F#H%Y6%.&99*(.&3(N%4EG$;H<;HIJ?KBZGRAW>JLCS ####!!#K&&%-X&#&,#%V%N6&%#&'%#+%.X,(.% ####E+%Y6%.&\T%V%N6&%4CS ####*5#4E+%Y6%.&\T.%(2G'%32%+.4C\T+%.X,(.%C ####] ########!!#^%&#&'%#&,&37#-%-,+1#3(2#%V%N6&*,(#&*-% ########E&,&37#F#3++314 ##########J]-%-,+1G6.3/%_J#FT#(6-0%+G5,+-3&44-%-,+1G/%&GX%3`G6.3/%4C#\#8>ZKAKG$BKHBG;>HaC#!#bcdeU#d ##########J]%V%N6&*,(G&*-%_J#FT#(6-0%+G5,+-3&4-*N+,&*-%4BHP;C#\#8>ZKAKG$BKHBGBR;U#gCDJ#.%N,(2.JCS ########!!#R(.%+&#&'%#&,&37.#*(&,#&'%#+%.X,(.% ########E+%Y6%.&\T+%.X,(.%#F#.&+G+%X73N%43++31G`%1.4E&,&37CU#E&,&37U#E+%Y6%.&\T+%.X,(.%CS ####_ ####!"" #####"#:*.X731#&'%#+%Y6%.&#+%.X,(.%D #####"! ####%N',#E+%Y6%.&\T+%.X,(.%S Tutorials Tutorials in this guide Tutorials written elsewhere Ellisgl's KO3 tutorial on dealtaker.com: 1. Install and Basic Usage 2. Views 3. Controllers 4. Models 5. Subrequests 6. Routes 7. Helpers 8. Modules 9. Vendor Libraries Simple example of controller model and view working together. 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 !"##$#%!&'(&)*+, passed to -$./0/112023. This will convert PHP errors into exceptions which are easier to handle. 1. An Improved Exception Handler Our custom exception handler is self explanatory. 45/%%&-$./0/6,74"832$0&"73"09%&-$./0/6-$./0/6,74"832$0&: &&&&8;<524&%3/324&=;0432$0&./095"#>,74"832$0&?" &&&&: &&&&&&&&2=&>-$./0/11A,B,CDEF,G)&'''&-$./0/11?"0H2#$0I"03 &&&&&&&&: &&&&&&&&&&&&8/#"0311./095"#>?"J &&&&&&&&K &&&&&&&&"5%" &&&&&&&&: &&&&&&&&&&&&3#L &&&&&&&&&&&&: &&&&&&&&&&&&&&&&-$./0/11?5$MN(/99>C$M11,**D*O&8/#"03113"73>?"J &&&&&&&&&&&&&&&&?/33#2<;3"%&'&/##/L &&&&&&&&&&&&&&&&> &&&&&&&&&&&&&&&&&&&&!/432$0!&&'(&PQQO &&&&&&&&&&&&&&&&&&&&!I"%%/M"!&'(&#/R;#5"04$9">?"N(M"3F"%%/M"> &&&&&&&&&&&&&&&&J &&&&&&&&&&&&&&&&2=&>?"&20%3/04"$=&S))E6,74"832$0 &&&&&&&&&&&&&&&&: &&&&&&&&&&&&&&&&&&&&?/33#2<;3"%T!/432$0!U&'&?"N(M"3V$9">J &&&&&&&&&&&&&&&&K &&&&&&&&&&&&&&&&WW&,##$#&%;<N#"X;"%3Y &&&&&&&&&&&&&&&&"4.$&*"X;"%311=/43$#L>*$;3"11M"3>!"##$#!N(;#2>?/33#2<;3"% &&&&&&&&&&&&&&&&N("7"4;3"> &&&&&&&&&&&&&&&&N(%"096."/9"#%> &&&&&&&&&&&&&&&&N(<$9L>J &&&&&&&&&&&&K &&&&&&&&&&&&4/34.&>,74"832$0&?" &&&&&&&&&&&&: &&&&&&&&&&&&&&&&WW&V5"/0&3."&$;38;3&<;=="#&2=&$0"&"72%3% &&&&&&&&&&&&&&&&$<6M"365"H"5>&/09&$<645"/0>J &&&&&&&&&&&&&&&&WW&A2%85/L&3."&"74"832$0&3"73 &&&&&&&&&&&&&&&&"4.$&8/#"03113"73>?"J &&&&&&&&&&&&&&&&WW&,723&R23.&/0&"##$#&%3/3;% &&&&&&&&&&&&&&&&"723>ZJ &&&&&&&&&&&&K &&&&&&&&K &&&&K K 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 !""#$%&'()*+,- 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 !""#$.(/),-/($%&'()*+,- was thrown. So this: *01,23-(23!""#$%&'()*+,-$4546789+:(3;,(/3-,*3(&+/*7<3=11=>6789+:(73?37A=+=7BBC would display a nice 404 error page, where: *01,23-(23D,0=-=$%&'()*+,-67E+1('*,1>38;+13FG/*3H(321+*=H:(7< 333333333333=11=>678;+173?3E(HGI88)=*06D,0=-=88J'='0($;+1BBBC would display an error 500 page. The Route .,G*(88/(*67(11,17<37(11,1KL='*+,-6KLF(//=I(B7<3=11=>67='*+,-73?37M5NOPQQ7<37F(//=I(73?37RQ7BB N;(9=G:*/6=11=>6 33337',-*1,::(173?37(11,1$0=-;:(17 BBC 2. The Error Page Controller )GH:+'39G-'*+,-3H(9,1(6B S 3333)=1(-*88H(9,1(6BC 3333J*0+/N*(F):=*(N)=I(3?3T.U88/+*(61=2G1:;(',;(6.(VG(/*88J+-+*+=:NG1+6BBBC 3333KK3W-*(1-=:31(VG(/*3,-:>X 3333+936.(VG(/*88J+-+*+=:3X??3.(VG(/*88J'G11(-*B 3333S 33333333+936JF(//=I(3?31=2G1:;(',;(6J*0+/N1(VG(/*N)=1=F67F(//=I(7BBB 33333333S 333333333333J*0+/N*(F):=*(NF(//=I(3?3JF(//=I(C 33333333Y 3333Y 3333(:/( 3333S 33333333J*0+/N1(VG(/*N='*+,-6454BC 3333Y 3333J*0+/N1(/),-/(N/*=*G/66+-*B3J*0+/N1(VG(/*N='*+,-6BBC Y 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: (11,1K454K(F=+:Z[5>,G1Z[5:,I+-Z[5+-9,1F=*+,-Z[5*,Z[50='\(1Z45I,,I:(R',F )GH:+'39G-'*+,-3='*+,-$4546B S 3333J*0+/N*(F):=*(N*+*:(3?374543],*3^,G-;7C 3333KK3!(1(32(3'0('\3*,3/((3+93=34543'=F(391,F3,G132(H/+*(R3"0+/3=::,2/3*0( 3333KK32(HF=/*(13*,39+-;3H1,\(-3:+-\/3=-;3G);=*(3*0(F3+-3=3/0,1*(13=F,G-*3,93*+F(R 3333+936+//(*36J$_%.`%.M7!""#$.%^%.%.7PB3a]E3/*1/*16J$_%.`%.M7!""#$.%^%.%.7P<3J$_%.`%.M7_%.`%.$] 3333S 33333333KK3_(*3=3:,'=:39:=I3/,32(3'=-3;+/):=>3;+99(1(-*3F(//=I(/3+-3,G13*(F):=*(R 33333333J*0+/N*(F):=*(N:,'=:3?3".T%C 3333Y !!!!""!#$$%!&'(')*!+,-./ !!!!0'12*345.*6,7*.34*'(')*89:9;< = 6)>?2+!)7+'2,7!(+'2,7AB:C8; D !!!!0'12*34'.E6?('.34'2'?.!F!GH(27'.7(7+.!H,-.G< = 6)>?2+!)7+'2,7!(+'2,7AB::8; D !!!!0'12*34'.E6?('.34'2'?.!F!GI7'.57(?!&.5J.5!K55,5G< = 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: '15,L!7.L!#$$%AKM+.6'2,7AB:C8G$1.!L.>*2'.!2*!-,L7G;< http://kerkness.ca/wiki/doku.php?id=routing:static_pages http://kerkness.ca/wiki/doku.php?id=routing:multi-language_with_a_route Clean URLs Removing 27-.M/616 from your urls. To keep your URLs clean, you will probably want to be able to access your app without having "27-.M/616" in the URL. There are two steps to remove 27-.M/616 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 27-.MA2?. setting of Kohana::init to false: N,1(7(OO272'8(55(P8 !!!!G>(*.A)5?G!!!F4!G"EP(66"GQ !!!!G27-.MA2?.G!F4!RST&KQ ;;< 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 "EP(66" instead of "EP(66"27-.M/616". 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 .M(E6?./1'(++.** to only /1'(++.** and alter the U.L52'.V(*. line to match the >(*.A)5? setting from your Kohana::init U.L52'.V(*.!"EP(66" The rest of the /1'(++.**!2?. 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: !"#$%&"!'(")*+,-.//(%0.&%123415'("63676&"489:);)<=>? Instead, we can try a slash: !"#$%&"!'(")*+.//(%0.&%123415'("63676&"48A);)<=>? If that doesn't work, try changing: !"#$%&"!'(")BC)%25"DB/E/AFG)<HI To something more simple: !"#$%&"!'(")BC)%25"DB/E/)<HI Still Failed! If you are still getting errors, check to make sure that your host supports URL 415J$"#$%&". If you can change the Apache configuration, add these lines to the the configuration, usually E&&/5B012K: LM%$"0&1$7)NAO.$A###AE&4(A47.//NP ))))Q$5"$).((1#>5"27 ))))R((1#)K$14).(( ))))R((1#QO"$$%5")R(( LAM%$"0&1$7P 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: (10.&%12)A)S ))))%25"D)))))%25"DB/E/)%25"DBE&4()%25"DBE&4T ))))&$7JK%("6)F'$%)%25"DB/E/T U (10.&%12)V)%25"DB/E/)S ))))%20('5")))))))K.6&0W%B012KT ))))K.6&0W%J/.66))XYZBGBGBX-[GGGT ))))K.6&0W%J%25"D)%25"DB/E/T U If you are having issues getting this working, enable debug level logging in nginx and check the access and error logs. Sharing Kohana Because Kohana follows a [front controller] pattern, which means that all requests are sent to %25"DB/E/, the filesystem is very configurable. Inside of %25"DB/E/ you can change the F.//(%0.&%12, F415'("6, and F676&"4 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 BE&.00"66 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 F.//(%0.&%12 variable lets you set the directory that contains your application files. By default, this is .//(%0.&%12. The F415'("6 variable lets you set the directory that contains module files. The F676&"4 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: !!!" ####$%&'()*+* ####,**-$.,/$0%" ####10&2-'3" ####343/'1" You could move the directories out of the web root so they look like this: ,**-$.,/$0%" 10&2-'3" 343/'1" !!!" ####$%&'()*+* Then you would need to change the settings in $%&'()*+* to be: 5,**-$.,/$0%#6#7))",**-$.,/$0%78 510&2-'3#####6#7))"10&2-'378 5343/'1######6#7))"343/'178 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): ,**3" ####900:,;" ########,**-$.,/$0%" ########!!!" ####:,<:,;" ########,**-$.,/$0%" ########!!!" =0+,%," ####>)?)" ####>)?)A" ####>)?)B" 10&2-'3" And you would need to change the settings in $%&'()*+* to be: 5,**-$.,/$0%#6#7))",**-$.,/$0%78 5343/'1######6#7))"))"))"=0+,%,">)?)78 510&2-'3#####6#7))"))"))"=0+,%,"10&2-'378 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 $%&'()*+* files. 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 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 C$/0;$,- and run C$/#$%$/. This will create the bare structure for a new git repository. Next, we will create a submodule for the 343/'1 directory. Go to http://github.com/kohana/core and copy the "Clone URL": Now use the URL to create the submodule for !"!#$%: &'#(!)*%+,)-$(.,,(&'#/00&'#1)*23+%04+1.5.03+6$2&'#(!"!#$% 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: &'#(!)*%+,)-$(.,,(&'#/00&'#1)*23+%04+1.5.0,.#.*.!$2&'#(%+,)-$!0,.#.*.!$ After submodules are added, they must be initialized: &'#(!)*%+,)-$('5'# Now that the submodules are added, you can commit them: &'#(3+%%'#(7%(89,,$,('5'#'.-(!)*%+,)-$!8 Next, create the application directory structure. This is the bare minimum required: %4,'6(7:(.::-'3.#'+503-.!!$!0;3+5#6+--$6<%+,$-= %4,'6(7:(.::-'3.#'+50;3+5>'&<?'$!= %4,'6(7%(ABBB(7:(.::-'3.#'+50;3.31$<-+&!= If you run >'5,(.::-'3.#'+5 you should see this: .::-'3.#'+5 .::-'3.#'+503.31$ .::-'3.#'+503+5>'& .::-'3.#'+503-.!!$! .::-'3.#'+503-.!!$!03+5#6+--$6 .::-'3.#'+503-.!!$!0%+,$- .::-'3.#'+50-+&! .::-'3.#'+50?'$! We don't want git to track log or cache files, so add a 2&'#'&5+6$ file to each of the directories. This will ignore all non- hidden files: $31+(8CD2EF8(G(.::-'3.#'+50;-+&!<3.31$=02&'#'&5+6$ Git ignores empty directories, so adding a 2&'#'&5+6$ file also makes sure that git will track the directory, but not the files within it. Now we need the '5,$H2:1: and *++#!#6.:2:1: files: &$#(1##:!/00&'#1)*23+%04+1.5.04+1.5.06.0I2J0%.!#$60'5,$H2:1:(775+731$3473$6#'>'3.#$ &$#(1##:!/00&'#1)*23+%04+1.5.04+1.5.06.0I2J0%.!#$60.::-'3.#'+50*++#!#6.:2:1:(775+731$3473$6#'>'3.#$ Commit these changes too: &'#(.,,(.::-'3.#'+5 &'#(3+%%'#(7%(89,,$,('5'#'.-(,'6$3#+6"(!#6)3#)6$8 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.: &'#(!)*%+,)-$(.,,(&'#/00&'#1)*23+%0!1.,+1.5,0!:6'&2&'#(%+,)-$!0!:6'& 2. then init and update the submodules: !"#$%&'()*&+,$"-"# !"#$%&'()*&+,$&.*/#, Updating Submodules At some point you will probably also want to upgrade your submodules. To update all of your submodules to the latest 0123 version: !"#$%&'()*&+,$4)5,/67$8!"#$67,69)&#$:;<=(/%#,5$>>$!"#$.&++$)5"!"-$:;<=(/%#,58 To update a single submodule, for example, %?%#,(: 6*$%?%#,( !"#$67,69)&#$:;<=(/%#,5 !"#$.&++$)5"!"-$:;<=(/%#,5 6*$;; !"#$/**$%?%#,( !"#$6)(("#$($8A.*/#,*$%?%#,($#)$+/#,%#$B,5%")-8 If you want to update a single submodule to a specific commit: 6*$()*&+,%=*/#/'/%, !"#$.&++$)5"!"-$:;<=(/%#,5 !"#$67,69)&#$4'4*,/CDCE<F'CGD6<:6:*CC*<'6D4G'',*/E6E' 6*$;;=;; !"#$/**$*/#/'/%, !"#$6)(("#$($8A.*/#,*$*/#/'/%,$()*&+,8 Note that you can also check out the commit at a tagged official release point, for example: !"#$67,69)&#$B:;<;E Simply run !"#$#/! 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: H%&'()*&+,$I()*&+,%=/&#7IJ ./#7$K$()*&+,%=/&#7 &5+$K$!"#L==!"#7&';6)(=9)7/-/=/!"# 2. open .git/config and remove the reference to the to submodule\ H%&'()*&+,$I()*&+,%=/&#7IJ &5+$K$!"#L==!"#7&';6)(=9)7/-/=/!"# 3. run git rm --cached path/to/submodule, e.g. !"#$5($6/67,*$()*&+,%=/&#7 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: !"#$%&'()*&+,$%?-6 3. run !"#$"%"# to update the project's repository configuration with the new URLs: !"#$&'()*+',-$"%"# 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/ Auth User authentication and authorization is provided by the auth module. The auth module is included with the Kohana 3.0 install, but needs to be enabled before you can use it. To enable, open your .//,"0.#"*%1(**#&#2./3/4/ file and modify the call to Kohana::modules by including the auth module like so: 5*4.%.66)*+',-&7.22.87 $$$$333 $$$$9.'#49$:;$<=>?AB39.'#49C $$$$333 DDE Next, you will then need to configure the auth module. The auth module provides the Auth::File driver for you. There is also an auth driver included with the ORM module. As your application needs change you may need to find another driver or develop your own. Configuration The default configuration file is located in <=>?AB1.'#410*%F"!1.'#43/4/. You should copy this file to ???AB10*%F"!1.'#43/4/ and make changes there, in keeping with the cascading filesystem. Config merging allows these default configuration settings to apply if you don't overwrite them in your application configuration file. Name Type Default Description driver &#2"%! file The name of the auth driver to use. hash_method &#2"%! sha256 The hashing function to use on the passwords. hash_key &#2"%! NULL The key to use when hashing the password. lifetime "%# 1209600 The time (in seconds) that the user's session is valid. session_type &#2"%! Session::$default The type of session to use when storing the auth user. session_key &#2"%! auth_user The name of the session variable used to save the user. Log in and out The auth module provides methods to help you log users in and out of your application. Log in The Auth::login method handles the login. 11$B.%+,-+$F2*)$.$F*2)$G"#4$"%/'#&$G"#4$%.)-&$-).",$1$/.&&G*2+ H/*&#$:$H#4"&I;2-J'-&#I;/*&#7DE H&'00-&&$:$'#466"%&#.%0-7DI;,*!"%7H/*&#K9-).",9LC$H/*&#K9/.&&G*2+9LDE "F$7H&'00-&&D M $$$$11$N*!"%$&'00-&&F',C$&-%+$#*$.// O -,&- M !!!!""!#$%&'!()&*+,-!.+',!/)01!2$!($34!5&26!+33$3!4+..)%+ 7 Logged in User There are two ways to check if a user is logged in. If you just need to check if the user is logged in use Auth::logged_in. &(!89:26;;&'.2)'0+8<=>*$%%+,?&'8<< !!!!""!A.+3!&.!*$%%+,!&'-!0$'2&':+!$' 7 +*.+ !!!!""!A.+3!&.'B2!*$%%+,!&'-!3+,&3+02!2$!26+!*$%&'!($34C 7 You can also get the logged in user object by using Auth::get_user. If the user is null, then no user was found. D:.+3!E!9:26;;&'.2)'0+8<=>%+2?:.+38<F ""!G6+01!($3!)!:.+3!8HA##!&(!'$2!:.+3!&.!($:',< &(!8D:.+3!IEE!':**< !!!!!""!A.+3!&.!($:',-!0$'2&':+!$' 7 +*.+ !!!!""!A.+3!5).!'$2!($:',-!3+,&3+02!2$!26+!*$%&'!($34 7 Log out The Auth::logout method will take care of logging out a user. 9:26;;&'.2)'0+8<=>*$%$:28<F ""!J+,&3+02!26+!:.+3!/)01!2$!*$%&'!K)%+ File Driver The Auth::File driver is included with the auth module. Below are additional configuration options that can be set for this driver. Name Type Default Description users )33)L array() A user => password (hashed) array of all the users in your application Forcing Login Auth_File::force_login allows you to force a user login without a password. ""!M$30+!26+!:.+3!5&26!)!:.+3')4+!$(!),4&'!2$!/+!*$%%+,!&'2$!L$:3!)KK*&0)2&$' 9:26;;&'.2)'0+8<=>($30+?*$%&'8B),4&'B<F D:.+3!E!9:26;;&'.2)'0+8<=>%+2?:.+38<F Developing Drivers Real World Example Sometimes the best way to learn is to jump right in and read the code from another module. The ORM module comes with an auth driver you can learn from. We will be developing an !"#$%&! driver. In your own driver you will substitute !"#$%&! with your driver name. This example file would be saved at '(((')*+,&#--!-+#./0+!"#$%&!1%0% (or 234(')* if you are creating a module). Quick Example First we will show you a quick example and then break down what is going on. ,&#--5'./067"#$%&!5!"/!89-5'./0 : 5555%;</!,/!95=.8,/><856&<?>8A.-!;8#$!B5A%#--C<;9B5A;!$!$D!;E 5555: 55555555++54<5.-!;8#$!+%#--C<;95,0!,F50!;! 5555G 5555%.D&>,5=.8,/><85%#--C<;9A.-!;8#$!E 5555: 55555555++5H!/.;85/0!5%#--C<;95=<;5/0!5.-!;8#$! 5555G 5555%.D&>,5=.8,/><85,0!,F6%#--C<;9A%#--C<;9E 5555: 55555555++5I0!,F5/<5-!!5>=5/0!5&<??!95>85.-!;50#-5/0!5?>J!85%#--C<;9 5555G 5555%.D&>,5=.8,/><85&<??!96>8A;<&!5K5LMNNE 5555: 55555555++5I0!,F5/<5-!!5>=5/0!5.-!;5>-5&<??!95>8B5#895>=5A;<&!5>-5-!/B50#-5#&&5;<&!- 5555G 5555%.D&>,5=.8,/><85?!/6.-!;A9!=#.&/5K5LMNNE 5555: 55555555++5O!/5/0!5&<??!95>85.-!;B5<;5;!/.;85/0!5A9!=#.&/5>=5#5.-!;5>-58</5=<.89 5555G G Extending Auth All drivers must extend the Auth class. ,&#--5'./067"#$%&!5!"/!89-5'./0 Abstract Methods The './0 class has 3 abstract methods that must be defined in your new driver. #D-/;#,/5%;</!,/!95=.8,/><856&<?>8A.-!;8#$!B5A%#--C<;9B5A;!$!$D!;EP #D-/;#,/5%.D&>,5=.8,/><85%#--C<;9A.-!;8#$!EP #D-/;#,/5%.D&>,5=.8,/><85,0!,F6%#--C<;9A.-!;EP Extending Functionality Given that every auth system is going to check if users exist and if they have roles or not you will more than likely have to change some default functionality. Here are a few functions that you should pay attention to. %.D&>,5=.8,/><85&<??!96>8A;<&!5K5LMNNE %.D&>,5=.8,/><85?!/6.-!;A9!=#.&/5K5LMNNE Activating the Driver After you create your driver you will want to use it. It is a easy as setting the !"#$%" configuration option to the name of your driver (in our case %&'()*%). About Kohana Cache Kohana_Cache provides a common interface to a variety of caching engines. Cache_Tagging is supported where available natively to the cache system. Kohana Cache supports multiple instances of cache engines through a grouped singleton pattern. Supported cache engines APC (Cache_Apc) File (Cache_File) Memcached (Cache_Memcache) Memcached-tags (Cache_Memcachetag) SQLite (Cache_Sqlite) Wincache Introduction to caching Caching should be implemented with consideration. Generally, caching the result of resources is faster than reprocessing them. Choosing what, how and when to cache is vital. PHP APC is one of the fastest caching systems available, closely followed by Memcached. SQLite and File caching are two of the slowest cache methods, however usually faster than reprocessing a complex set of instructions. Caching engines that use memory are considerably faster than file based alternatives. But memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching. [!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions. What the Kohana Cache module does (and does not do) This module provides a simple abstracted interface to a wide selection of popular PHP cache engines. The caching API provides the basic caching methods implemented across all solutions, memory, network or disk based. Basic key / value storing is supported by all drivers, with additional tagging and garbage collection support where implemented or required. Kohana Cache does not provide HTTP style caching for clients (web browsers) and/or proxies (Varnish, Squid). There are other Kohana modules that provide this functionality. Choosing a cache provider Getting and setting values to cache is very simple when using the Kohana Cache interface. The hardest choice is choosing which cache engine to use. When choosing a caching engine, the following criteria must be considered: 1. Does the cache need to be distributed? This is an important consideration as it will severely limit the options available to solutions such as Memcache when a distributed solution is required. 2. Does the cache need to be fast? In almost all cases retrieving data from a cache is faster than execution. However generally memory based caching is considerably faster than disk based caching (see table below). 3. How much cache is required? Cache is not endless, and memory based caches are subject to a considerably more limited storage resource. Driver Storage Speed Tags Distributed Automatic Garbage Collection Notes APC Memory Excellent No No Yes Widely available PHP opcode caching solution, improves php execution performance Wincache Memory Excellent No No Yes Windows variant of APC File Disk Poor No No No Marginally faster than execution Memcache (tag) Memory Good No (yes) Yes Yes Generally fast distributed solution, but has a speed hit due to variable network latency and serialization Sqlite Disk Poor Yes No No Marginally faster than execution It is possible to have hybrid cache solutions that use a combination of the engines above in different contexts. This is supported with Kohana Cache as well Minimum requirements Kohana 3.0.4 PHP 5.2.4 or greater Kohana Cache configuration Kohana Cache uses configuration groups to create cache instances. A configuration group can use any supported driver, with successive groups using multiple instances of the same driver type. The default cache group is loaded based on the !"#$%&&'(%)"*+, setting. It is set to the )-+% driver as standard, however this can be changed within the ."//+-#",-01.2003,4"/5/$/ file. 6..6!$"17%6,$%6(%)"*+,6#"#$%6(4-8%46,069%9#"#$% 6!"#$%&&'(%)"*+,6:6;9%9#"#$%;< 6..6=0"(6,$%69%9#"#$%6#"#$%6(4-8%46*3-176(%)"*+,63%,,-17 6'9%9#"#$%6:6!"#$%&&-13,"1#%>?< Group settings Below are the default cache configuration groups for each supported driver. Add to- or override these settings within the "//+-#",-01.#01)-7.#"#$%5/$/ file. Name Required Description driver YES (string) The driver type to use default_expire NO (string) The driver type to use ;)-+%;66:6"44"A > 6666;(4-8%4;6666666666666:6;)-+%;B 6666;#"#$%C(-4;6666666666:6DEEEDFG5;#"#$%.5H0$"1"C#"#$%;B 6666;(%)"*+,C%I/-4%;66666:6JKLLB ?B Memcache & Memcached-tag settings Name Required Description driver YES (string) The driver type to use servers YES (array) Associative array of server details, must include a host key. (see Memcache server configuration below) compression NO (boolean) Use data compression when caching Memcache server configuration Name Required Description host YES (string) The host of the memcache server, i.e. localhost; or 127.0.0.1; or memcache.domain.tld port NO (integer) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default 11211 persistent NO (boolean) Controls the use of a persistent connection. Default TRUE weight NO (integer) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default 1 timeout NO (integer) Value in seconds which will be used for connecting to the daemon. Think twice timeout NO (integer) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default 1 retry_interval NO (integer) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default 15 status NO (boolean) Controls if the server should be flagged as online. Default TRUE failure_callback NO (callback) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default NULL !"#"$%$&#!'()'%**%+ , ''''!-*./#*!'''''''''''''()'!"#"$%$&#!0 ''''!-#1%2345#67.*#!'''''()'89::0 ''''!$;"7*#<<.;=!''''''''()'>?AB0''''''''''''''CC'D<#'E3.F'$;"7*#<<.;=' '''''''''''''''''''''''''''''''''''''''''''''''',$%='$%2<#'.<<2#<'G.4&'.=4#H#*<I ''''!<#*/#*<!''''''''''''()'%**%+ '''', ''''''''%**%+ '''''''', ''''''''''''!&;<4!'''''''''''''()'!3;$%3&;<4!0''CC'J#"$%$&#'A#*/#* ''''''''''''!7;*4!'''''''''''''()'KKLKK0''''''''CC'J#"$%$&#'7;*4'=2"F#* ''''''''''''!7#*<.<4#=4!'''''''()'>?AB0''''''''CC'M#*<.<4#=4'$;==#$4.;= ''''''''I0 ''''I0 I0 !"#"$%$&#4%H!'()'%**%+ , ''''!-*./#*!'''''''''''''()'!"#"$%$&#4%H!0 ''''!-#1%2345#67.*#!'''''()'89::0 ''''!$;"7*#<<.;=!''''''''()'>?AB0''''''''''''''CC'D<#'E3.F'$;"7*#<<.;=' '''''''''''''''''''''''''''''''''''''''''''''''',$%='$%2<#'.<<2#<'G.4&'.=4#H#*<I ''''!<#*/#*<!''''''''''''()'%**%+ '''', ''''''''%**%+ '''''''', ''''''''''''!&;<4!'''''''''''''()'!3;$%3&;<4!0''CC'J#"$%$&#'A#*/#* ''''''''''''!7;*4!'''''''''''''()'KKLKK0''''''''CC'J#"$%$&#'7;*4'=2"F#* ''''''''''''!7#*<.<4#=4!'''''''()'>?AB0''''''''CC'M#*<.<4#=4'$;==#$4.;= ''''''''I0 ''''I0 I0 APC settings !%7$!''''''()'%**%+ , ''''!-*./#*!'''''''''''''()'!%7$!0 ''''!-#1%2345#67.*#!'''''()'89::0 I0 SQLite settings !<N3.4#!'''()'%**%+ , ''''!-*./#*!'''''''''''''()'!<N3.4#!0 ''''!-#1%2345#67.*#!'''''()'89::0 ''''!-%4%F%<#!'''''''''''()'?MMM?OPQ!$%$&#CR;&%=%S$%$&#Q<N38!0 ''''!<$&#"%!'''''''''''''()'!TUB?OB'O?VB'$%$&#<,.-'W?UTP?U,KLXI'MUYJ?UZ'[BZ0' ''''''''''''''''''''''''''''''''''''''4%H<'W?UTP?U,L\\I0'#67.*%4.;='Y]OB^BU0'$%$&#'OB_OI!0 I0 File settings !"#$%!&&&&'(&)**)+ , &&&&!-*#.%*!&&&&&&&&&&&&&'(&!"#$%!/ &&&&!0)01%2-#*!&&&&&&&&&&'(&!0)01%34561)7)20)01%!/ &&&&!-%")8$92%:;#*%!&&&&&'(&<=>>/ ? Wincache settings !#70)01%!&'(&)**)+ , &&&&!-*#.%*!&&&&&&&&&&&&&'(&!#70)01%!/ &&&&!-%")8$92%:;#*%!&&&&&'(&<=>>/ ?/ Override existing configuration group The following example demonstrates how to override an existing configuration setting, using the config file in 3);;$#0)9#673067"#A30)01%4;1;. BC;1;&-%"#7%-,!DEDFGHI!?&6*&-#%,!J6&-#*%09&K0*#;9&)00%KK4!?L *%98*7&)**)+ , &&&&33&M.%**#-%&91%&-%")8$9&067"#A8*)9#67 &&&&!N%N0)01%!&&&'(&)**)+ &&&&, &&&&&&&&!-*#.%*!&&&&&&&&&'(&!N%N0)01%!/&&33&OK%&P%N0)01%-&)K&91%&-%")8$9&-*#.%* &&&&&&&&!-%")8$92%:;#*%!&'(&Q>>>/&&&&&&&&33&M.%*#-%&-%")8$9&%:;#*+ &&&&&&&&!K%*.%*K!&&&&&&&&'(&)**)+ &&&&&&&&, &&&&&&&&&&&&33&G--&)&7%&K%*.%* &&&&&&&&&&&&)**)+ &&&&&&&&&&&&, &&&&&&&&&&&&&&&&!16K9!&&&&&&&'(&!0)01%4-6N)#749$-!/ &&&&&&&&&&&&&&&&!;6*9!&&&&&&&'(&RRSRR/ &&&&&&&&&&&&&&&&!;%*K#K9%79!&'(&TGUDV &&&&&&&&&&&&? &&&&&&&&?/ &&&&&&&&!06N;*%KK#67!&&&&'(&TGUDV &&&&? ?L Add new configuration group The following example demonstrates how to add a new configuration setting, using the config file in 3);;$#0)9#673067"#A30)01%4;1;. BC;1;&-%"#7%-,!DEDFGHI!?&6*&-#%,!J6&-#*%09&K0*#;9&)00%KK4!?L *%98*7&)**)+ , &&&&33&M.%**#-%&91%&-%")8$9&067"#A8*)9#67 &&&&!")K95.!&&&'(&)**)+ &&&&, &&&&&&&&!-*#.%*!&&&&&&&&&'(&!);0!/&&33&OK%&P%N0)01%-&)K&91%&-%")8$9&-*#.%* &&&&&&&&!-%")8$92%:;#*%!&'(&R>>>/&&&33&M.%*#-%&-%")8$9&%:;#*+ &&&&? ?L Kohana Cache usage Kohana_Cache provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in Kohana Cache additionally provide tagging and garbage collection where they are supported by the respective drivers. Getting a new cache instance Creating a new Kohana Cache instance is simple, however it must be done using the Cache::instance method, rather than the traditional !"# constructor. $%%$&'"()"$($!"#$*!+)(!,"$-.$,(,/"$0+*!1$)/"$2".(03)$1'-04 $5,(,/"$6$&(,/"77*!+)(!,"89: The default group will use whatever is set to Cache::$default and must have a corresponding configuration definition for that group. To create a cache instance using a group other than the default, simply provide the group name as an argument. $%%$&'"()"$($!"#$*!+)(!,"$-.$)/"$;";,(,/"$1'-04 $5;";,(,/"$6$&(,/"77*!+)(!,"8<;";,(,/"<9: If there is a cache instance already instantiated then you can get it directly from the class member. [!!] Beware that this can cause issues if you do not test for the instance before trying to access it. $%%$&/",=$.-'$)/"$">*+)(!,"$-.$)/"$,(,/"$2'*?"' $*.$8*++")8&(,/"775*!+)(!,"+<;";,(,/"<A99 $B $$$$$$%%$C")$)/"$">*+)*!1$,(,/"$*!+)(!,"$2*'",)3D$8.(+)"'9 $$$$$$5;";,(,/"$6$&(,/"775*!+)(!,"+<;";,(,/"<A: $E $"3+" $B $$$$$$%%$C")$)/"$,(,/"$2'*?"'$*!+)(!,"$8+3-#"'9 $$$$$$5;";,(,/"$6$&(,/"77*!+)(!,"8<;";,(,/"<9: $E Setting and getting variables to and from cache The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification. [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources. Setting a value to cache Setting a value to cache using the Cache::set method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations. The first example demonstrates how to quickly load and set a value to the default cache instance. $%%$&'"()"$($,(,/(F3"$-FG",) $5-FG",)$6$!"#$+)2&3(++: $%%$H")$($4'-4"')D $5-FG",)IJ.--$6$<F('<: $%%$&(,/"$)/"$-FG",)$0+*!1$2".(03)$1'-04$8K0*,=$*!)"'.(,"9$#*)/$2".(03)$)*;"$8LMNN$+",-!2+9 $&(,/"77*!+)(!,"89IJ+")8<.--<O$5-FG",)9: If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below. $%%$H")$)/"$-FG",)$0+*!1$($2".*!"2$1'-04$.-'$($2".*!"2$)*;"$4"'*-2$8LN$+",-!2+9 $5;";,(,/"$6$&(,/"77*!+)(!,"8<;";,(,/"<9: $5;";,(,/"IJ+")8<.--<O$5-FG",)O$LN9: Setting a value with tags Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface. !""!#$%!&!'&'($!)*+%&*'$!%(&%!+,--./%+!%&0+ !12$2'&'($!3!4&'($55)*+%&*'$672$2'&'($%&0789 !""!:$+%!;./!%&00)*0!)*%$/;&'$ !);!612$2'&'($!)*+%&*'$.;!4&'($<:&00)*08 != !!!!!!""!>$%!&!?&,$!A)%(!+.2$!%&0+!;./!BC!+$'.*D+ !!!!!!12$2'&'($EF+$%67;..7G!1.HI$'%G!BCG!&//&J67+*&;,7G!7+%;,7G!7;,H&/7889 !K !""!L%($/A)+$!+$%!A)%(.,%!%&0+ !$+$ != !!!!!!""!>$%!&!?&,$!;./!BC!+$'.*D+ !!!!!!12$2'&'($EF+$%67;..7G!1.HI$'%G!BC89 !K It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the Cache_Tagging interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard. Getting a value from cache Getting variables back from cache is achieved using the Cache::get method using a single key to identify the cache entry. !""!M$%/)$?$!&!?&,$!;/.2!'&'($!6N,)'OJ8 !1.HI$'%!3!4&'($55)*+%&*'$68EF0$%67;..789 In cases where the requested key is not available or the entry has expired, a default value will be returned (NULL by default). It is possible to define the default value as the key is requested. !""!P;!%($!'&'($!O$J!)+!&?&)&H$!6A)%(!D$;&,%!?&,$!+$%!%.!QRS>T8 !);!61.HI$'%!3!4&'($55)*+%&*'$68EF0$%67;..7G!QRS>T88 != !!!!!!""!U.!+.2$%()*0 !K !$+$ != !!!!!!""!U.!+.2$%()*0!$+$ !K Getting values from cache using tags It is possible to retrieve values from cache grouped by tag, using the Cache::find method with drivers that support tagging. [!!] The Memcachetag driver does not support the 4&'($55;)*D61%&08 interface and will throw an exception. !""!#$%!&*!)*+%&*'$!.;!'&'($ !1'&'($!3!4&'($55)*+%&*'$672$2'&'($%&0789 !""!V/&-!)*!&!%/J"'&%'(!+%&%$2$*%!%.!0/&'$;,J!(&*D$!2$2'&'($%&0 !%/J != !!!!!!""!Q)*D!?&,$+!H&+$D!.*!%&0 !!!!!!/$%,/*!1'&'($EF;)*D67+*&;,789 !K !'&%'(!64&'($<TW'$-%).*!1$8 != !!!!!!""!X&*D$!0/&'$;,J !!!!!!/$%,/*!QRS>T9 !K Deleting values from cache Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories: Delete value by key. Deletes a cached value by the associated key. Delete all values. Deletes all caches values stored in the cache instance. Delete values by tag. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite. Delete value by key To delete a specific value by its associated key: !""!#$!%&'!()(&'!'*%+,!$-+!.$--.!/0!1'2'%'1 !/$!34)(&'55/*0%)*('36781'2'%'3.$--.66 !9 !!!!!!""!4)(&'!'*%+,!0:(('00$:22,!1'2'%'1;!1-!0-<'%&/*= !> By default a ?AB value will be returned. However a CDEFB value will be returned in instances where the key did not exist in the cache. Delete all values To delete all values in a specific instance: !""!#$!)22!()(&'!/%'<0!G&'+'!1'2'%'1!0:(('00$:22, !/$!34)(&'55/*0%)*('36781'2'%'H)22366 !9 !!!!!!!""!I-!0-<'%&/*= !> It is also possible to delete all cache items in every instance: !""!C-+!')(&!()(&'!/*0%)*(' !$-+')(&!34)(&'55J/*0%)*('0!)0!J=+-:K!L8!J/*0%)*('6 !9 !!!!!!/$!3J/*0%)*('781'2'%'H)22366 !!!!!!9 !!!!!!!!!!!M)+H1:<K3./*0%)*('!5!.NJ=+-:KN.!&)0!O''*!$2:0&'1P.6Q !!!!!!> !> Delete values by tag Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag. !""!R'%!()(&'!/*0%)*(' !J()(&'!L!4)(&'55/*0%)*('36Q !""!4&'(S!$-+!%)==/*=!/*%'+$)(' !/$!3J()(&'!/*0%)*('-$!4)(&'H?)==/*=6 !9 !!!!!!!""!I'2'%'!)22!'*%+/'0!O,!%&'!%)=!.0*)$:. !!!!!!!J()(&'781'2'%'H%)=3.0*)$:.6Q !> Garbage Collection Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable. When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below. !""!R'%!)!()(&'!/*0%)*(' !"#$#%&'()*&!+!,$#%&--)./0$.#&12()*&234 !55!6&0!$!7,!89:;$;)*)0<!:(!=>? !"#!+!=>4 !55!A(!0%&!7,!89:;$;)*)0<!)/!$!%)0 !)(!19$.B1>CDD3!E+!"#!$.B!"#$#%&'()*&!)./0$.#&:(!,$#%&'7$9;$&,:**&#03 !F !!!!!!55!7$9;$&!,:**&#0 !!!!!!"#$#%&'()*&GH$9;$&'#:**&#0134 !I Interfaces Kohana Cache comes with two interfaces that are implemented where the drivers support them: Cache_Tagging for tagging support on cache entries Cache_MemcacheTag Cache_Sqlite Cache_GarbageCollect for garbage collection with drivers without native support Cache_File Cache_Sqlite When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the $9;$&'#:**&#0 method. 55!,9&$0&!$!#$#%&!)./0$.#& "#$#%&!+!,$#%&--)./0$.#&134 55!J&/0!(:9!7$9;$&!,:**&#0):. )(!1"#$#%&!)./0$.#&:(!,$#%&'7$9;$&,:**&#03 F !!!!!55!,:**&#0!$9;$& !!!!!"#$#%&GH$9;$&'#:**&#0134 I Using Codebench The contents of this page are taken (with some minor changes) from http://www.geertdedeckere.be/article/introducing- codebench and are copyright Geert De Deckere. For a long time I have been using a quick-and-dirty ;&.#%K$9LM8%8 file to optimize bits of PHP code, many times regex- related stuff. The file contained not much more than a gettimeofday function wrapped around a (:9 loop. It worked, albeit not very efficiently. Something more solid was needed. I set out to create a far more usable piece of software to aid in the everlasting quest to squeeze every millisecond out of those regular expressions. Codebench Goals Benchmark multiple regular expressions at once Being able to compare the speed of an arbitrary amount of regular expressions would be tremendously useful. In case you are wondering??yes, I had been writing down benchmark times for each regex, uncommenting them one by one. You get the idea. Those days should be gone forever now. Benchmark multiple subjects at once What gets overlooked too often when testing and optimizing regular expressions is the fact that speed can vastly differ depending on the subjects, also known as input or target strings. Just because your regular expression matches, say, a valid email address quickly, does not necessarily mean it will quickly realize when an invalid email is provided. I plan to write a follow-up article with hands-on regex examples to demonstrate this point. Anyway, Codebench allows you to create an array of subjects which will be passed to each benchmark. Make it flexible enough to work for all PCRE functions Initially I named the module ??Regexbench?? . I quickly realized, though, it would be flexible enough to benchmark all kinds of PHP code, hence the change to ??Codebench?? . While tools specifically built to help profiling PCRE functions, like preg_match or preg_replace, definitely have their use, more flexibility was needed here. You should be able to compare all kinds of constructions like combinations of PCRE functions and native PHP string functions. Create clean and portable benchmark cases Throwing valuable benchmark data away every time I needed to optimize another regular expression had to stop. A clean file containing the complete set of all regex variations to compare, together with the set of subjects to test them against, would be more than welcome. Moreover, it would be easy to exchange benchmark cases with others. Visualize the benchmarks Obviously providing a visual representation of the benchmark results, via simple graphs, would make interpreting them easier. Having not to think about Internet Explorer for once, made writing CSS a whole lot more easy and fun. It resulted in some fine graphs which are fully resizable. Below are two screenshots of Codebench in action. !"#$%&'(#() is a class made for benchmarking different ways to validate hexadecimal HTML color values, e.g. *+++. If you are interested in the story behind the actual regular expressions, take a look at this topic in the Kohana forums. Benchmarking seven ways to validate HTML color values Collapsable results per subject for each method Working with Codebench Codebench is included in Kohana 3, but if you need you can download it from GitHub. Be sure Codebench is activated in your ",,#$-".$(/01((.2.)",3,4,. Creating your own benchmarks is just a matter of creating a class that extends the Codebench class. The class should go in -#"2252015/-4 and the class name should have the 65/-4& prefix. Put the code parts you want to compare into separate methods. Be sure to prefix those methods with 15/-4&, other methods will not be benchmarked. Glance at the files in 7(%8#520-(%515/-40-#"2252015/-40 for more examples. Here is another short example with some extra explanations. 009-#"2252015/-40#.)$7%$:$.23,4, -#"22965/-4&;.)$7<$:$.295=.5/%29'(%515/-49> 9999009?(759(,.$(/"#95=,#"/".()9-(775/.29"1(8.9.45915/-47")A9B$#53 9999009CDE;9"##(F5%39GH;29F$##9159-(/I5).5%9.(9#$/A29"8.(7".$-"##3 9999,81#$-9J%52-)$,.$(/9K9L'4(,,$/:9(BB9#5"%$/:9%$:$.2M9)5:5=9I29#.)$73LN 9999009C(F97"/9.$7529.(95=5-8.595"-4975.4(%9,5)9281O5-.3 9999009D(."#9#((,29K9#((,29P9/8715)9(B975.4(%29P9/8715)9(B9281O5-.2 9999,81#$-9J#((,29K9QRRRRRN 9999009D459281O5-.29.(928,,#9$.5)".$I5#9.(9(8)915/-47")A975.4(%23 9999,81#$-9J281O5-.29K9"))" 9999S 99999999LQTU%$:$.2LV 99999999L/(W%$:$.2LV 9999XN 9999,81#$-9B8/-.$(/915/-4&)5:5=SJ281O5-.X 9999> 99999999)5.8)/9,)5:&)5,#"-5SL0YZ%[0LV9LLV9J281O5-.XN 9999\ 9999,81#$-9B8/-.$(/915/-4&#.)$7SJ281O5-.X 9999> !!!!!!!!"#$%"&!'$"()*+,%-.#/$0!12334156 !!!!7 7 And the winner is? ltrim. Happy benchmarking! Database Kohana 3.0 comes with a robust module for working with databases. By default, the database module supports drivers for MySQL and PDO, but new drivers can be made for other database servers. The database module is included with the Kohana 3.0 install, but needs to be enabled before you can use it. To enable, open your 899'(/8$(:&;-::$,$"8939<9 file and modify the call to Kohana::modules by including the database module like so: =:<8&8>>):?%'#,*8""8* !!!!333 !!!!1?8$8-8,#1!AB!CDEFGHI31?8$8-8,#10 !!!!333 556 Next, you will then need to configure the database module to connect to your database. Once that is done then you can make queries and use the results. The database module also provides a config driver (for storing configuration in the database) and a session driver. Configuration The default config file is located in CDEFGHI;?8$8-8,#;/:&J(K;?8$8-8,#39<9. You should copy this file to GFFFGHI;/:&J(K;?8$8-8,#39<9 and make changes there, in keeping with the cascading filesystem. The database configuration file contains an array of configuration groups. The structure of each database configuration group, called an "instance", looks like this: ,$"(&K!LMNHGMOPQMGCP!AB!8""8* !!!!1$9#1!!!!!!!!!AB!,$"(&K!EGHGRGNPQHSFP0 !!!!1/:&&#/$(:&1!!!AB!8""8!ODMMPOHLDMQGTTGS0 !!!!1$8-'#Q9"#J(U1!AB!,$"(&K!HGRVPQFTPWLX0 !!!!1/<8",#$1!!!!!!AB!,$"(&K!OIGTGOHPTQNPH0 !!!!19":J('(&K1!!!!AB!-::'#8&!YZPTSQFTDWLVLM[0 50 Understanding each of these settings is important. INSTANCE_NAME Connections can be named anything you want, but you should always have at least one connection called "default". DATABASE_TYPE One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class. CONNECTION_ARRAY Specific driver options for connecting to your database. (Driver options are explained below.) TABLE_PREFIX Prefix that will be added to all table names by the query builder. Prepared statements will not use the table prefix. QUERY_PROFILING Enables profiling of database queries. This is useful for seeing how many queries each page is using, and which are taking the longest. You must enable the profiler the view these stats. Example The example file below shows 2 MySQL connections, one local and one remote. "#$%"&!8""8 * !!!!"#$%&'()"!*+!&,,&- !!!!. !!!!!!!!")-/$"!!!!!!!*+!"0-12("3 !!!!!!!!"4566$4)756"!*+!&,,&-. !!!!!!!!!!!!"851)6&0$"!!!*+!"(54&(851)"3 !!!!!!!!!!!!"'1$,6&0$"!!!*+!"#9'1$,"3 !!!!!!!!!!!!"/&11:5,#"!!!*+!"0-/&11:5,#"3 !!!!!!!!!!!!"/$,171)$6)"!*+!;<=>?3 !!!!!!!!!!!!"#&)&9&1$"!!!*+!"0-#96&0$"3 !!!!!!!!A3 !!!!!!!!")&9($/,$%7B"!*+!""3 !!!!!!!!"48&,1$)"!!!!!!*+!"')%C"3 !!!!!!!!"/,5%7(76D"!!!!*+!EFG?3 !!!!A3 !!!!",$05)$"!*+!&,,&-. !!!!!!!!")-/$"!!!!!!!*+!"0-12("3 !!!!!!!!"4566$4)756"!*+!&,,&-. !!!!!!!!!!!!"851)6&0$"!!!*+!"HHIHHIHHIHH"3 !!!!!!!!!!!!"'1$,6&0$"!!!*+!",$05)$'1$,"3 !!!!!!!!!!!!"/&11:5,#"!!!*+!"0-/&11:5,#"3 !!!!!!!!!!!!"/$,171)$6)"!*+!;<=>?3 !!!!!!!!!!!!"#&)&9&1$"!!!*+!"0-,$05)$#96&0$"3 !!!!!!!!A3 !!!!!!!!")&9($/,$%7B"!*+!""3 !!!!!!!!"48&,1$)"!!!!!!*+!"')%C"3 !!!!!!!!"/,5%7(76D"!!!!*+!EFG?3 !!!!A3 AJ Connections and Instances Each configuration group is referred to as a database instance. Each instance can be accessed by calling Database::instance. If you don't provide a parameter, the default instance is used. KK!E871!:5'(#!4566$4)!)5!)8$!#&)&9&1$!#$%76$#!&1!"#$%&'()" L#$%&'()!*!M&)&9&1$NN761)&64$.AJ KK!E871!:5'(#!4566$4)!)5!)8$!#&)&9&1$!#$%76$#!&1!",$05)$" L,$05)$!!*!M&)&9&1$NN761)&64$.",$05)$"AJ To disconnect the database, simply destroy the object: '61$).L#$%&'()A KK!O, '61$).M&)&9&1$NNL761)&64$1P"#$%&'()"QAJ If you want to disconnect all of the database instances at once: M&)&9&1$NNL761)&64$1!*!&,,&-.AJ Connection Settings Every database driver has different connection settings. MySQL A MySQL database can accept the following options in the 4566$4)756 array: Type Option Description Default value 1),76D hostname Hostname of the database (54&(851) 76)$D$, port Port number RG== 1),76D socket UNIX socket RG== !"#$%& username Database username '()) !"#$%& password Database password '()) *++,-.% persistent Persistent connections /0)12 !"#$%& database Database name 3+4.%. PDO A PDO database can accept these options in the 5+%%-5"$+% array: Type Option Description Default value !"#$%& dsn PDO data source identifier ,+5.,4+!" !"#$%& username Database username '()) !"#$%& password Database password '()) *++,-.% persistent Persistent connections /0)12 If you are using PDO and are not sure what to use for the 6!% option, review PDO::__construct. Making Queries There are two different ways to make queries. The simplest way to make a query is to use Database_Query, via DB::query, to manually create queries. These queries are called prepared statements and allow you to set query parameters which are automatically escaped. The second way to make a query is by building the query using method calls. This is done using the query builder. All queries are run using the -7-58"- method, which accepts a Database object or instance name. See Database_Query::execute for more information. Query Builder Creating queries dynamically using objects and methods allows queries to be written very quickly in an agnostic way. Query building also adds identifier (table and column name) quoting, as well as value quoting. At this time, it is not possible to combine query building with prepared statements. Select Each type of database query is represented by a different class, each with their own methods. For instance, to create a SELECT query, we use DB::select which is a shortcut to return a new Database_Query_Builder_Select object: 9:8-#;<=<>?!-,-5"ABC Query Builder methods return a reference to itself so that method chaining may be used. Select queries ussually require a table and they are referenced using the D#+EAB method. The D#+EAB method takes one parameter which can be the table name (string), an array of two strings (table name and alias), or an object (See Subqueries in the Advanced Queries section below). 9:8-#;<=<>?!-,-5"ABFGD#+EAH8!-#!HBC Limiting the results of queries is done using the I4-#-AB, .%6JI4-#-AB and +#JI4-#-AB methods. These methods take three parameters: a column, an operator, and a value. 9:8-#;<=<>?!-,-5"ABFGD#+EAH8!-#!HBFGI4-#-AH8!-#%.E-HK<H=HK<HL+4%HBC Multiple I4-#-AB methods may be used to string together multiple clauses connected by the boolean operator in the method's prefix. The I4-#-AB method is a wrapper that just calls .%6JI4-#-AB. 9:8-#;<=<>?!-,-5"ABFGD#+EAH8!-#!HBFGI4-#-AH8!-#%.E-HK<H=HK<HL+4%HBFG+#JI4-#-AH8!-#%.E-HK<H=HK<HL.%- You can use any operator you want. Examples include M', ?2NO22', G, =P, Q=, etc. Use an array for operators that require more than one value. 9:8-#;<=<>?!-,-5"ABFGD#+EAH8!-#!HBFGI4-#-AH,+&$%!HK<HP=HK<RBC !"#$%&'(')*++,$-$./01234%5607#,$%,712389$%$07-5:;<,7='737='>?1 !"#$%&'(')*++,$-$./01234%5607#,$%,712389$%$07#,$%<A6$7='7BC7='A%%A&07D59<7=76A%E7=76A//711 !"#$%&'(')*++,$-$./01234%5607#,$%,712389$%$07D5;<FA/$7='7*GHIGGC7='A%%A&0!/9$<='!<5811 By default, DB::select will select all columns (JGKGLH'M'NNN), but you can also specify which columns you want returned by passing parameters to DB::select: !"#$%&'(')*++,$-$./07#,$%<A6$7='7OA,,85%F71234%5607#,$%,712389$%$07#,$%<A6$7='7(7='7D59<71 Now take a minute to look at what this method chain is doing. First, we create a new selection object using the DB::select method. Next, we set table(s) using the 4%5601 method. Last, we search for a specific records using the 89$%$01 method. We can display the SQL that will be executed by casting the query to a string: $.95')$P#:++QA%,00,/%;<:1'!"#$%&1 RR'J95#-F'F;,O-A&+ RR'JGKGLH'S#,$%<A6$S='SOA,,85%FS'TUVW'S#,$%,S'IXGUG'S#,$%<A6$S'('7D59<7 Notice how the column and table names are automatically escaped, as well as the values? This is one of the key benefits of using the query builder. Select - AS (column aliases) It is also possible to create YJ aliases when selecting, by passing an array as each parameter to DB::select: !"#$%&'(')*++,$-$./0A%%A&07#,$%<A6$7='7#71='A%%A&07OA,,85%F7='7O711234%5607#,$%,71 This query would generate the following SQL: JGKGLH'S#,$%<A6$S'YJ'S#S='SOA,,85%FS'YJ'SOS'TUVW'S#,$%,S Select - DISTINCT Unique column values may be turned on or off (default) by passing TRUE or FALSE, respectively, to the F;,/;<./01 method. !"#$%&'(')*++,$-$./07#,$%<A6$7123F;,/;<./0HUZG1234%5607O5,/,71 This query would generate the following SQL: JGKGLH')BJHBCLH'S#,$%<A6$S'TUVW'SO5,/,S Select - LIMIT & OFFSET When querying large sets of data, it is often better to limit the results and page through the data one chunk at a time. This is done using the -;6;/01 and 544,$/01 methods. !"#$%&'(')*++,$-$./01234%560SO5,/,S123-;6;/0[?123544,$/0\?1 This query would generate the following SQL: JGKGLH'M'TUVW'SO5,/,S'KBWBH'[?'VTTJGH'\? Select - ORDER BY Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple 5%F$%]P&01 methods can be used to add additional sorting capability. !"#$%&'(')*++,$-$./01234%560SO5,/,S1235%F$%]P&0SO#P-;,9$FS='S)GJLS1 This query would generate the following SQL: JGKGLH'M'TUVW'SO5,/,S'VU)GU'*^'SO#P-;,9$FS')GJL For a complete list of methods available while building a select query see Database_Query_Builder_Select. Insert To create records into the database, use DB::insert to create an INSERT query, using !"#$%&'( to pass in the data: )*$%+,-.-/01123&%+4'5$&%+&56-"++",'5$&%+3"7%56-58"&&9:+;5((<=!"#$%&'"++",'5>+%;56-58?&ABC;5((D This query would generate the following SQL: EFGHCI-EFIJ-K$&%+&K-'K$&%+3"7%K6-K8"&&9:+;K(-LMNOHG-'5>+%;56-58?&ABC;5( For a complete list of methods available while building an insert query see Database_Query_Builder_Insert. Update To modify an existing record, use DB::update to create an UPDATE query: )*$%+,-.-/011$8;"4%'5$&%+&5(<=&%4'"++",'5$&%+3"7%5-.=-5P"3%5((<=9Q%+%'5$&%+3"7%56-5.56-5P:Q35(D This query would generate the following SQL: OR/MIH-K$&%+&K-GHI-K$&%+3"7%K-.-5P"3%5-ASHCH-K$&%+3"7%K-.-5P:Q35 For a complete list of methods available while building an update query see Database_Query_Builder_Update. Delete To remove an existing record, use DB::delete to create a DELETE query: )*$%+,-.-/011;%#%4%'5$&%+&5(<=9Q%+%'5$&%+3"7%56-5EF56-"++",'5P:Q356-5P"3%5((D This query would generate the following SQL: /HNHIH-TCJU-K$&%+&K-ASHCH-K$&%+3"7%K-EF-'5P:Q356-5P"3%5( For a complete list of methods available while building a delete query see Database_Query_Builder_Delete. Advanced Queries Joins Multiple tables can be joined using the P:23'( and :3'( methods. The P:23'( method takes two parameters. The first is either a table name, an array containing the table and alias, or an object (subquery or expression). The second parameter is the join type: LEFT, RIGHT, INNER, etc. The :3'( method sets the conditions for the previous P:23'( method and is very similar to the 9Q%+%'( method in that it takes three parameters; left column (name or object), an operator, and the right column (name or object). Multiple :3'( methods may be used to supply multiple conditions and they will be appended with an 'AND' operator. VV-IQ2&-*$%+,-92##->23;-"##-4Q%-8:&4&-+%#"4%;-4:-W&724QW-924Q-XJEF )*$%+,-.-/011&%#%Y4'5"$4Q:+&Z3"7%56-58:&4&ZY:34%345(<=>+:7'5"$4Q:+&5(<=P:23'58:&4&5(<=:3'5"$4Q:+&Z2;5 This query would generate the following SQL: GHNH\I-K"$4Q:+&KZK3"7%K6-K8:&4&KZKY:34%34K-TCJU-K"$4Q:+&K-XJEF-K8:&4&K-JF-'K"$4Q:+&KZK2;K-.-K8:&4&KZK If you want to do a LEFT, RIGHT or INNER JOIN you would do it like this P:23'5Y:#$7[3"7%56-54,8%[:>[P:235(: VV-IQ2&-*$%+,-92##->23;-"##-4Q%-8:&4&-+%#"4%;-4:-W&724QW-924Q-NHTI-XJEF )*$%+,-.-/011&%#%Y4'(<=>+:7'5"$4Q:+&5(<=P:23'58:&4&56-5NHTI5(<=:3'5"$4Q:+&Z2;56-5.56-58:&4&Z"$4Q:+[2; This query would generate the following SQL: GHNH\I-K"$4Q:+&KZK3"7%K6-K8:&4&KZKY:34%34K-TCJU-K"$4Q:+&K-NHTI-XJEF-K8:&4&K-JF-'K"$4Q:+&KZK2;K-.-K8:& When joining multiple tables with similar column names, it's best to prefix the columns with the table name or table alias to avoid errors. Ambiguous column names should also be aliased so that they can be referenced easier. Database Functions Eventually you will probably run into a situation where you need to call !"#$% or some other database function within your query. The query builder supports these functions in two ways. The first is by using quotes within aliases: &'()*+,-,./001)2)3456**6+57!"#$%58(1)*96:)8;7<,74=462>(1)*17;;?A*=:57(1)*17;B This looks almost exactly the same as a standard CD alias, but note how the column name is wrapped in double quotes. Any time a double-quoted value appears inside of a column name, only the part inside the double quotes will be escaped. This query would generate the following SQL: DEFE!%,!"#$%5G(1)*96:)G;,CD,G4=462>(1)*1G,HI"J,G(1)*1G When building complex queries and you need to get a count of the total rows that will be returned, build the expression with an empty column list first. Then clone the query and add the COUNT function to one copy and the columns list to the other. This will cut down on the total lines of code and make updating the query easier. &'()*+,-,./001)2)345;?A*=:57(1)*17; ,,,,?K=L957M=1417;?=957M=141N(1)*96:)7<,7-7<,7(1)*1N(1)*96:)7; ,,,,?OP)*)57(1)*1N634LQ)7<,7-7<,%I#E; ,,,,?OP)*)57M=141N3*)64)R7<,7-7<,&+)14)*R6+;B &4=462,-,32=9),&'()*+B &4=462?1)2)3456**6+57!"#$%5,.SD%S$!%,8(1)*96:)8;7<,7(9L'()>(1)*17;;B &'()*+?1)2)3457M=141N(1)*96:)7;?RL14L9345;B Aggregate Functions Aggregate functions like !"#$%5;, D#J5;, CTU5;, etc. will most likely be used with the V*=(M>W+5; and possibly the P6QL9V5; methods in order to group and filter the results on a set of columns. &'()*+,-,./001)2)3457(1)*96:)7<,6**6+57!"#$%58LR8;7<,74=462>M=1417; ,,,,?A*=:57M=1417;?V*=(M>W+57(1)*96:)7;?P6QL9V574=462>M=1417<,7-7<,XY;B This will generate the following query: DEFE!%,G(1)*96:)G<,!"#$%5GLRG;,CD,G4=462>M=141G,HI"J,GM=141G,UI"#Z,/[,G(1)*96:)G,\CTS$U,G4=462>M=141G Subqueries Query Builder objects can be passed as parameters to many of the methods to create subqueries. Let's take the previous example query and pass it to a new query. &1(W,-,./001)2)3457(1)*96:)7<,6**6+57!"#$%58LR8;7<,74=462>M=1417; ,,,,?A*=:57M=1417;?V*=(M>W+57(1)*96:)7;?P6QL9V574=462>M=1417<,7-7<,XY;B &'()*+,-,./001)2)3457M*=AL2)1N]7<,7M=141N4=462>M=1417;?A*=:57M*=AL2)17; ,,,,?K=L956**6+5&1(W<,7M=1417;<,7S$$EI7;?=957M*=AL2)1N(1)*96:)7<,7-7<,7M=141N(1)*96:)7;B This will generate the following query: DEFE!%,GM*=AL2)1GN]<,GM=141GNG4=462>M=141G,HI"J,GM*=AL2)1G,S$$EI,^"S$ 5,DEFE!%,G(1)*96:)G<,!"#$%5GLRG;,CD,G4=462>M=141G,HI"J,GM=141G,UI"#Z,/[,G(1)*96:)G,\CTS$U,G4=462>M=14 "$,GM*=AL2)1GNG(1)*96:)G,-,GM=141GNG(1)*96:)G Insert queries can also use a select query for the input values &1(W,-,./001)2)3457(1)*96:)7<,6**6+57!"#$%58LR8;7<,74=462>M=1417; ,,,,?A*=:57M=1417;?V*=(M>W+57(1)*96:)7;?P6QL9V574=462>M=1417<,7-7<,XY;B &'()*+,-,./00L91)*457M=14>4=46217<,6**6+57(1)*96:)7<,7M=1417;;?1)2)345&1(W;B This will generate the following query: !"#$%&'!"&(')*+,-.-+-/0,)'1)2,345/63)7')*+,-,)8' #$9$:&')2,345/63)7':(;"&1)<=)8'>#')-+-/0.*+,-,)'?%(')*+,-,)'A%(;B'CD')2,345/63)'E>F!"A')-+-/0.*+,-,) Boolean Operators and Nested Clauses Multiple Where and Having clauses are added to the query with Boolean operators connecting each expression. The default operator for both methods is AND which is the same as the and_ prefixed method. The OR operator can be specified by prefixing the methods with or_. Where and Having clauses can be nested or grouped by post fixing either method with _open and then followed by a method with a _close. KL234M'H'NCOO,303P-18QGR4+61S2,34,S8 ''''QGTU343.+*3518 ''''''''QG+4.TU3431S<=S7'S!"S7'K3V*<43=8 ''''''''QG/5=.TU343.+*3518 ''''''''''''QGTU3431S0/,-.0+W<5S7'SXHS7'K0/,-.6+5-U8 ''''''''''''QG+4.TU3431S0/,-.0+W<5S7'S!#S7'";998 ''''''''QG/5=.TU343.P0+,318 ''''QGTU343.P0+,318 ''''QG/5=.TU3431S436+Y3=S7S!#S7'";998Z This will generate the following query: #$9$:&'['?%(')2,34,)'\E$%$'1')<=)'!"'1I7']7'^7'_8'(%'1')0/,-.0+W<5)'XH'I]`aJ]JbJ_'(%')0/,-.0+W<5)'!# Database Expressions There are cases were you need a complex expression or other database functions, which you don't want the Query Builder to try and escape. In these cases, you will need to use a database expression created with DB::expr. A database expression is taken as direct input and no escaping is performed. KL234M'H'NCOO2*=/-31S2,34,S8QG,3-1/44/M1S0+W<5.P+25-S'HG'NCOO3V*41S0+W<5.P+25-'c'IS888QGTU3431S<=S7'S This will generate the following query, assuming K<='H'd_: ;BN>&$')2,34,)'#$&')0+W<5.P+25-)'H')0+W<5.P+25-)'c'I'\E$%$')<=)'H'd_ Another example to calculate the distance of two geographical points: KL234M'H'NCOO,303P-1/44/M1NCOO3V*41S=3W433,1/P+,1,<514/=</5,1SeK0/-eS88'[',<514/=</5,1)0/-<-2=3)88'c' You must validate or escape any user input inside of DB::expr as it will obviously not be escaped it for you. Executing Once you are done building, you can execute the query using 3V3P2-318 and use the results. K43,20-'H'KL234MQG3V3P2-318Z To use a different database config group pass either the name or the config object to 3V3P2-318. K43,20-'H'KL234MQG3V3P2-31SP+5R<W.5/63S8 Results Execute Once you have a query object built, either through a prepared statement or through the builder, you must then 3V3P2-318 the query and retrieve the results. Depending on the query type used, the results returned will vary. Select DB::select will return a Database_Result object which you can then iterate over. This example shows how you can iterate through the Database_Result using a foreach. !"#$%&'$()(*+,,$#&#-'./012"34.5%$#"$5/0167#"#.58#"929#:5;(5)5;(</01#=#-%'#./> 23"#?-7.!"#$%&'$(?$(!%$#"/ ((((AA(B#C:("#49C:#"(#4?9&('3(!%$#"D5#4?9&5E ((((#-73(!%$#"D5#4?9&5EFG(C##:$('3(8#"92H(79$A7#"(?--3%C'ICG> J Select - !"#$%&'()*+ and !"#!""$(*+ When iterating over a result set, the default type will be an associative array with the column names or aliases as the keys. As an option, before calling #=#-%'#./, you can specify to return the result rows as an object by using the ?$K3LM#-'./ method. The ?$K3LM#-'./ method takes one parameter, the name of the class of your choice, but will default to TRUE which uses the $':N&?$$. Here is the example again using $':N&?$$. !"#$%&'$()(*+,,$#&#-'./012"34.5%$#"$5/0167#"#.58#"929#:5;(5)5;(</01?$K3LM#-'./01#=#-%'#./> 23"#?-7.!"#$%&'$(?$(!%$#"/ ((((AA(B#C:("#49C:#"(#4?9&('3(!%$#"01#4?9& ((((#-73(!%$#"01#4?9&FG(C##:$('3(8#"92H(79$A7#"(?--3%C'ICG> J The method ?$K?$$3-./ will remove the object name and return the results set back to an associative array. Since this is the default, this method is seldom required. Select - !"#!,,!-*+ Sometimes you will require the results as a pure array rather than as an object. The *?'?L?$#KO#$%&' method ?$K?""?H./ will return an array of all rows. !"#$%&'$()(*+,,$#&#-'.59:5;(5#4?9&5/012"34.5%$#"$5/01#=#-%'#./> !%$#"$()(!"#$%&'$01?$K?""?H./> 23"#?-7.!%$#"$(?$(!%$#"/ ((((#-73(5P$#"(Q*,(5F!%$#"D59:5E> ((((#-73(5P$#"(R4?9&,(5F!%$#"D5#4?9&5E> J It also accepts two parameters that can be very helpful: !S#H and !8?&%#. When passing a value to !S#H you will index the resulting array by the column specified. !"#$%&'$()(*+,,$#&#-'.59:5;(5#4?9&5/012"34.5%$#"$5/01#=#-%'#./> !%$#"$()(!"#$%&'$01?$K?""?H.59:5/> 23"#?-7.!%$#"$(?$(!9:()1(!%$#"/ ((((#-73(5P$#"(Q*,(5F!9:> ((((#-73(5P$#"(R4?9&,(5F!%$#"D5#4?9&5E> J The second parameter, !8?&%#, will reference the column specified and return that value rather than the whole row. This is particularly useful when making T$#&#-'1 dropdowns. !"#$%&'$()(*+,,$#&#-'.59:5;(5C?4#5/012"34.5%$#"$5/01#=#-%'#./> !%$#"$()(!"#$%&'$01?$K?""?H.59:5;5C?4#5/> AA(B736(?(:"3U:36C(69'7(?&&(%$#"$(9C(9'F #-73(V3"4,,$#&#-'.5?%'73"5;(!%$#"$/ To return a non-associative array, leave !S#H as NULL and just pass a !8?&%#. !"#$%&'$()(*+,,$#&#-'.5#4?9&5/012"34.5%$#"$5/01#=#-%'#./> !%$#"$()(!"#$%&'$01?$K?""?H.WPXX;(5#4?9&5/> 23"#?-7.!%$#"$(?$(!#4?9&/ ((((#-73(5P$#"(R4?9&,(5F!#4?9&> J Select - !"#$% Sometime you only want a single value from a query. The !"#$% method returns the value of the named column from the current row. The second parameter, &'"()*+#, is used to supply a default value when the result is NULL. &#,#)+-*."/.0102344."+"5#$)//)6$789:;<$=*."/>)?"=%707#,#)+-*."/.7%%AB(/,?$7*."/.7%AB"C"5*#"$%AB!"#$7 Select - &'&(")$% The mysql database driver returns a 2)#)F)."-G".*+# that works with a MySQL Resource data type. Since this resource lives outside of PHP environment, it can't be serialized which means it also can't be cached. To get around this the 2)#)F)."-G".*+# object has the 5)5H"'$% method that returns a 2)#)F)."-G".*+#-8)5H"' object of the result set. The 2)#)F)."-G".*+#-8)5H"' can be serialized and cached, but can take up more memory. NOTE: Currently, the PDO diver always returns a class of 2)#)F)."-G".*+#-8)5H"', so 5)5H"'$% just returns itself. The 5)5H"'$% function doesn't actually do any caching, it simply returns the result in a way that can be serialized and cached. You will need to use the Cache Module or some other caching method. Select - &*+,#$% The 2)#)F)."-G".*+# object implements the 8,*>#)F+" Interface. The method 5,*>#$% returns the total row count in the result set. NOTE: This is the count of the current result set, not a count of how many records are in the database. This is important to point out especially when using +I?I#$% and ,((."#$% in your query. For a complete list of methods available when working with a result set see Database_Result. Insert DB::insert returns an array of two values: the last insert id and the number of affected rows. &I>."/#0102344I>."/#$7#,,+.7% 0000AB5,+*?>.$)//)6$7>)?"707?,'"+707'".5/IJ#I,>7%% 0000ABK)+*".$)//)6$7LMI+0NODD0PD=0<)F+"0L)Q707NODD707R,Q"/(*+0PS0)?J0?,#,/E0Q"I!H.0T*.#0SOAJ,*>'.7% +I.#$&I>."/#-I'0&)(("5#"'-/,Q.%010&I>."/#AB"C"5*#"$%E Update & Delete DB::update and DB::delete both return the number of affected rows as an integer. &/,Q.-'"+"#"'0102344'"+"#"$7#,,+.7%ABQH"/"$7?,'"+707+IM"707NODD7%AB"C"5*#"$%E Examples Here are some "real world" examples of using the database library to construct your queries and use the results. Examples of Prepared Statements TODO: 4-6 examples of prepared statements of varying complexity, including a good bind() example. Pagination and search/filter In this example, we loop through an array of whitelisted input fields and for each allowed non-empty value we add it to the search query. We make a clone of the query and then execute that query to count the total number of results. The count is then passed to the Pagination class to determine the search offset. The last few lines search with Pagination's items_per_page and offset values to return a page of results based on the current page the user is on. &U*"/60102344."+"5#$%AB(/,?$7*."/.7%E !!"#$%&'()*+,&-"*&.,('(&-/($0' 1-"*23/#45.'&6&)**)%78-/*'.3#)2(89&8$)'.3#)2(89&8(2)/$8:; -"*()+,&71-"*23/#45.'&)'&1#)2(:& < &&&&1=)$5(&6&>**??(.713ABC9&1#)2(9&D>EFB:; &&&&/-&71=)$5(&G66&D>EFB&>HI&1=)$5(&G6&88: &&&&< &&&&&&&&1J5(*%KLM,(*(71#)2(9&8$/N(89&8O8P1=)$5(P8O8:; &&&&Q Q !!+"4%&.,(&J5(*%&R&(S(+5.(&/. 14)/#)./"#3J5(*%&6&+$"#(&1J5(*%; 1+"5#.&6&14)/#)./"#3J5(*%KL'($(+.78TUVHC7WXW:&>F&2%+"5#.8:KL(S(+5.(7:KL(.782%+"5#.8:; !!4)''&.,(&.".)$&/.(2&+"5#.&."&Y)/#)./"# 1+"#-/&6&Z",)#)??1+"#-/KL$")0784)/#)./"#8:; 14)/#)./"#&6&Y)/#)./"#??-)+."*%7)**)%7 &&&&8.".)$3/.(2'8&6L&1+"5#.9 &&&&8+5**(#.34)(8&&&6L&)**)%78'"5*+(8&6L&8*"5.(89&8N(%8&6L&84)(8:9& &&&&8/.(2'34(*34)(8&6L&[\9 &&&&8=/(M8&&&&&&&&&&&6L&84)/#)./"#!4*(..%89 &&&&8)5."3,/0(8&&&&&&6L&C]VB9 ::; 14)(3$/#N'&6&14)/#)./"#KL*(#0(*7:; !!'()*+,&-"*&*('5$.'&'.)*./#&).&.,(&"--'(.&+)$+5$).(0&^%&.,(&Y)/#)./"#&+$)'' 1J5(*%KL"*0(*3^%78$)'.3#)2(89&8)'+8: &&&&KL"*0(*3^%78-/*'.3#)2(89&8)'+8: &&&&KL$/2/.714)/#)./"#KL/.(2'34(*34)(: &&&&KL"--'(.714)/#)./"#KL"--'(.:; 1*('5$.'&6&1J5(*%KL(S(+5.(7:KL)'3)**)%7:; Having TODO: example goes here We could use more examples on this page. ORM Kohana 3.x includes a powerful Object Relational Mapping (ORM) module that uses the active record pattern and database introspection to determine a model's column information. ORM is integrated tightly with the Validation library. The ORM allows for manipulation and control of data within a database as though it was a PHP object. Once you define the relationships ORM allows you to pull data from your database, manipulate the data in any way you like, and then save the result back to the database without the use of SQL. By creating relationships between models that follow convention over configuration, much of the repetition of writing queries to create, read, update, and delete information from the database can be reduced or entirely removed. All of the relationships can be handled automatically by the ORM library and you can access related data as standard object properties. ORM is included with the Kohana 3.x install but needs to be enabled before you can use it. In your )44$/+)./"#!^"".'.*)4P4,4 file modify the call to Kohana::modules and include the ORM modules. Getting started Before we use ORM, we must enable the modules required Z",)#)??2"05$('7)**)%7 &&&&PPP &&&&80).)^)'(8&6L&_UIY>C`P80).)^)'(89 &&&&8"*28&6L&_UIY>C`P8"*289 &&&&PPP ::; The database module is requried for the ORM module to work. Of course the database module has to be configured to use an existing database. You can now create your models and use ORM. Creating your Model To create a model for the table !"!#"$% in your database, create the file &''()*&+),-.*(&%%"%.!,/"(.!"!#"$0'1' with the following syntax: *(&%%23,/"(43"!#"$2"5+"-/%2673 8 2222000 9 (this should provide more examples) Overriding the Table name If you wish to change the database table that a model uses, just override the :4+&#("4-&!" variable like this: '$,+"*+"/2:4+&#("4-&!"2;2<%+$&-="4+&#("-&!"<> Changing the primary key ORM assumes each model (and database table) has an )/ column that is indexed and unique. If your primary key column isn't named )/, that's fine - just override the :4'$)!&$?4"? variable like this: '$,+"*+"/2:4'$)!&$?4"?2;2<%+$&-="4'"?<> Use a non-default database For each model, you can define which database configuration ORM will run queries on. If you override the :4/#4=$,A' variable in your model, ORM will connect to that database. Example: '$,+"*+"/2:4/#4=$,A'2;2<&(+"$-&+"<> Basic Usage Load a new model instance To create a new 3,/"(4B%"$ instance, you can do one of two things: :A%"$2;2673CCD&*+,$?E<A%"$<F> ..26$ :A%"$2;2-"G23,/"(4B%"$EF> Inserting To insert a new record into the database, create a new instance of the model: :A%"$2;2673CCD&*+,$?E<A%"$<F> Then, assign values for each of the properties; :A%"$HID)$%+4-&!"2;2<J$"-+<> :A%"$HI(&%+4-&!"2;2<7"K-,$<> :A%"$HI*)+?2;2<3"$*"$<> :A%"$HI%+&+"2;2<LM<> Insert the new record into the database by running ORM::save: !"#$%&'#()$*+, ORM::save checks to see if a value is set for the primary key (-. by default). If the primary key is set, then ORM will execute an /01234 otherwise it will execute an 567483. Finding an object To find an object you can call the ORM::find method or pass the id into the ORM constructor: 99:;-<.:"#$%:=->?:51:A !"#$%:B:C8DEEF(G>H%I*J"#$%J+ ::::&'=?$%$*J-.JK:JBJK:A+ ::::&'F-<.*+, 99:C% !"#$%:B:C8DEEF(G>H%I*J"#$%JK:A+, Check that ORM loaded a record Use the ORM::loaded method to check that ORM successfully loaded a record. -F:*!"#$%&'LH(.$.*++ M ::::99:NH(.:=(#:#"GG$##F"L O $L#$ M ::::99:4%%H% O Updating and Saving Once an ORM model has been loaded, you can modify a model's properties like this: !"#$%&'F-%#>P<(Q$:B:R3%$<>R, !"#$%&'L(#>P<(Q$:B:R8$S<H%R, And if you want to save the changes you just made back to the database, just run a #()$*+ call like this: !"#$%&'#()$*+, Deleting To delete an object, you can call the ORM::delete method on a loaded ORM model. !"#$%:B:C8DEEF(G>H%I*J"#$%JK:A+, !"#$%&'.$L$>$*+, Relationships Kohana ORM supports four types of object relationships: T$LH<U#P>H, ?(#PQ(<I, ?(#PQ(<I:R>?%H"U?R and ?(#PH<$. The ?(#PQ(<I:R>?%H"U?R relationship can be used to function like Active Record's ?(#PQ(<IP(<.PT$LH<U#P>H relationship type. belongs_to A T$LH<U#P>H relation should be used when you have one model that belongs to another. For example, a V?-L. model belongs_to a 0(%$<> or a ;L(U model T$LH<U#P>H a VH"<>%I. This is the base T$LH<U#P>H relationship: W%H>$G>$.:!PT$LH<U#P>H:B:(%%(I* ::::JX(L-(#:<(Q$YJ:B':(%%(I* !!!!!!!!"#$%&'"!!!!!!!()!"*#$%&'!+,#&-". !!!!!!!!"/$0&12+34&5"!()!"*6$'7#+-". !!!!8. 89 You can omit any or all of the keys/values in the array on the right, in which case defaults are used: :0$;&6;&%!<3=&'$+2>3;$!(!,00,5?"*,'1,>!+,#&-"!()!,00,5?889 The alias name is what is used to access the related model in your code. If you had a $>; model that belonged to a A>&0 model and wished to use the default values of the =&'$+2>3;$ configuration then your code would look like this: :0$;&6;&%!<3=&'$+2>3;$!(!,00,5?"7>&0"!()!,00,5?889 To access the user model, you would use <:$>;B)7>&0. Since we're using the defaults above, the alias name will be used for the model name, and the foreign key in the posts table will be the alias name followed by 31%, in this case it would be 7>&031%. (You can change the 31% suffix by modifying the </$0&12+34&53>7//1C variable in the model.) Let's say your $>; database table schema doesn't have a 7>&031% column but instead has an ,7;D$031% column which is a foreign key for a record in the A>&0 table. You could use code like this: :0$;&6;&%!<3=&'$+2>3;$!(!,00,5? !!!!"7>&0"!()!,00,5? !!!!!!!!"/$0&12+34&5"!()!",7;D$031%". !!!!8. 89 If you wanted access a post's author by using code like <:$>;B),7;D$0 then you would simply need to change the alias and add the #$%&' index: :0$;&6;&%!<3=&'$+2>3;$!(!,00,5? !!!!",7;D$0"!()!,00,5? !!!!!!!!"#$%&'"!!!!!!!()!"7>&0". !!!!8. 89 has_many The standard D,>3#,+5 relationship will likely fall on the other side of a =&'$+2>3;$ relationship. In the above examples, a post belongs to a user. From the user's perspective, a user has many posts. A has_many relationship is defined below: :0$;&6;&%!<3D,>3#,+5!(!,00,5? !!!!"*,'1,>!+,#&-"!()!,00,5? !!!!!!!!"#$%&'"!!!!!!!()!"*#$%&'!+,#&-". !!!!!!!!"/$0&12+34&5"!()!"*6$'7#+-". !!!!8. 89 Again, you can omit all keys in the right array to use the defaults: :0$;&6;&%!<3D,>3#,+5!(!,00,5?"*,'1,>!+,#&-"!()!,00,5?889 For our user and post example, this would look like the following in the user model: :0$;&6;&%!<3D,>3#,+5!(!,00,5?":$>;>"!()!,00,5?889 Using the above, the posts could be access using <7>&0B):$>;>B)/1+%3,''?8. Notice the /1+%3,''?8 used in this example. With =&'$+2>3;$ and D,>3$+& relationship types, the model is already loaded with necessary data. For D,>3#,+5 relationships, however, you may want to limit the number of results or add additional conditions to the SQL query; you can do so prior to the /1+%3,''?8. The model name used by default will be the singular name of the alias using the 1+/'&6;$0 class. In this case, :$>;> uses :$>; as the model name. The foreign key used by default is the owner model's name followed by 31%. In this case, the foreign key will be 7>&031% and it must exist in the posts table as before. Let's assume now you want to access the posts using the name !"#$%&! instead, and are still using the '(")#$*%+ key as in the ,&-#./!*"# example. You would define your has_many relationship as: 0$#"&1"&+23*)'!*4'.5262'$$'57 22228!"#$%&!82692'$$'57 2222222284#+&-8222222269280#!"8: 222222228;#$&%/.*<&5826928'(")#$*%+8: 2222=: => has_one A )'!*#.& relationship is almost identical to a )'!*4'.5 relationship. In a )'!*#.& relationship, there can be 1 and only 1 relationship (rather than 1 or more in a has_many). If a user can only have one post or story, rather than many then the code would look like this: 0$#"&1"&+23*)'!*#.&262'$$'57 22228!"#$582692'$$'57 2222222284#+&-8222222269280#!"8: 222222228;#$&%/.*<&5826928'(")#$*%+8: 2222=: => has_many "through" A )'!*4'.52?")$#(/)? relationship is used for many-to-many relationships. For instance, let's assume now we have an additional model, called '"&/#$5. Posts may belong to more than one category, and each category may have more than one post. To link them together, an additional table is needed with columns for a 0#!"*%+ and a 1'"&/#$5*%+ (sometimes called a pivot table). We'll name the model for this A#!"*'"&/#$5 and the corresponding table 1'"&/#$%&!*0#!"!. To define the )'!*4'.5 "through" relationship, the same syntax for standard has_many relationships is used with the addition of a 'through' parameter. Let's assume we're working with the Post model: 0$#"&1"&+23*)'!*4'.5262'$$'57 222281'"&/#$%&!82692'$$'57 2222222284#+&-822269281'"&/#$58: 222222228")$#(/)8269281'"&/#$%&!*0#!"!8: 2222=: => In the Category model: 0$#"&1"&+23*)'!*4'.5262'$$'57 222280#!"!82692'$$'57 2222222284#+&-822269280#!"8: 222222228")$#(/)8269281'"&/#$%&!*0#!"!8: 2222=: => To access the categories and posts, you simply use 30#!"B91'"&/#$%&!B9;%.+*'--7= and 31'"&/#$5B90#!"!B 9;%.+*'--7= Methods are available to check for, add, and remove relationships for many-to-many relationships. Let's assume you have a $post model loaded, and a $category model loaded as well. You can check to see if the $post is related to this $category with the following call: 30#!"B9)'!781'"&/#$%&!8:231'"&/#$5=> The first parameter is the alias name to use (in case your post model has more than one relationship to the category model) and the second is the model to check for a relationship with. Assuming you want to add the relationship (by creating a new record in the categories_posts table), you would simply do: !"#$%&'())*+,(%-.#/0-$+12!,(%-.#/345 To remove: !"#$%&'/-6#7-*+,(%-.#/0-$+12!,(%-.#/345 Validation ORM models are tightly integrated with the Validation library and the module comes with a very flexible ORM_Validation_Exception that helps you quickly handle validation errors from basic CRUD operations. Defining Rules Validation rules are defined in the 89:;;/<=-$*4 method. This method returns the array of rules to be added to the Validation object like so: "<>=0,2?<,%0#2/<=-$*4 A 2222/-%</2(//(3* 22222222+<$-/(6-+2B'2(//(3* 222222222222CC2D$-$2E(=0);;#%F-6"%3*!7(=<-45 222222222222(//(3*+#%F-6"%3+41 222222222222CC2G(==$2H#6-FG=($$;;$#6-F6-%I#)*+"(/(6J+12+"(/(6K+45 222222222222(//(3*+H#6-FG=($$;;$#6-F6-%I#)+12(//(3*+"(/(6J+12+"(/(6K+441 222222222222CC2G(==$2LFG=($$;;(F6-%I#)*!7(=<-45 222222222222(//(3*(//(3*+LFG=($$+12+(F6-%I#)+441 222222222222CC2G(==$2%I-2=(6>)(2?<,%0#2()2"($$-$2%I-2?0-=)27(=<-2()2%I-27(=0)(%0#2#>M-,% 222222222222(//(3*?<,%0#*!7(=<-12E(=0)(%0#2!#>M-,%4 222222222222A 2222222222222222!#>M-,%&'-//#/*+$#6-F?0-=)+12+$#6-F-//#/+45 222222222222N12(//(3*+;7(=<-+12+;7(=0)(%0#+441 2222222241 222245 N Bound Values ORM will automatically bind the following values with E(=0)(%0#;;>0)*4: :field - The name of the field the rule is being applied to. :value - The value of the field the rule is being applied to. :model - The instance of the model that is being validated. Automatic Validation All models automatically validate their own data when 89:;;$(7-*4, 89:;;<")(%-*4, or 89:;;,/-(%-*4 is called. Because of this, you should always expect these methods to throw an ORM_Validation_Exception when the model's data is invalid. "<>=0,2?<,%0#2(,%0#F,/-(%-*4 A 2222%/3 2222A 22222222!<$-/2B289:;;?(,%#/3*+<$-/+45 22222222!<$-/&'<$-/(6-2B2+07(=0)2<$-/(6-+5 22222222!<$-/&'$(7-*45 2222N 2222,(%,I2*89:FE(=0)(%0#FOP,-"%0#2!-4 2222A 22222222!-//#/$2B2!-&'-//#/$*45 2222N N Handling Validation Exceptions The ORM_Validation_Exception will give you access to the validation errors that were encountered while trying to save a model's information. The !"#$%&'()&*(+,$-./01*(+,22033+3456 method works very similarly to %&'()&*(+,22033+3456. Not passing a first parameter will return the name of the rules that failed. But unlike %&'()&*022033+3456, the first parameter of !"#$%&'()&*(+,$-./01*(+,22033+3456 is a directory path. The model's ORM::$_object_name will be appended to the directory in order to form the message file for %&'()&*(+,22033+3456 to use. The second parameter is identical to that of %&'()&*(+,22033+3456. In the below example, the error messages will be defined in &11'(/&*(+,78044&90478+)0'47:403;1<1 1:='(/>?:,/*(+,>&/*(+,$/30&*056 >>>>*3A >>>> >>>>>>>>B:403>C>!"#22?&/*+3A5D:403D6E >>>>>>>>B:403FG:403,&80>C>D(,H&'()>:403,&80DE >>>>>>>>B:403FG4&H056E >>>>I >>>>/&*/<>5!"#$%&'()&*(+,$-./01*(+,>B06 >>>> >>>>>>>>B033+34>C>B0FG033+345D8+)0'4D6E >>>>I I External Validation Certain forms contain information that should not be validated by the model, but by the controller. Information such as a CSRF token, password verification, or a CAPTCHA should never be validated by a model. However, validating information in multiple places and combining the errors to provide the user with a good experience is often quite tedius. For this reason, the ORM_Validation_Exception is built to handle multiple Validation objects and namespaces the array of errors automatically for you. !"#224&H056, !"#22:1)&*056, and !"#22/30&*056 all take an optional first parameter which is a Validation object to validate along with the model. 1:='(/>?:,/*(+,>&/*(+,$/30&*056 >>>>*3A >>>> >>>>>>>>B:403>C>!"#22?&/*+3A5D:403D6E >>>>>>>>B:403FG:403,&80>C>B$J!KLMD:403,&80DNE >>>>>>>>B:403FG1&44O+3)>C>B$J!KLMD1&44O+3)DNE >>>>>>>>B0.*3&$3:'04>C>%&'()&*(+,22?&/*+3A5B$J!KL6 >>>>>>>>>>>>FG3:'05D1&44O+3)$/+,?(38DP>D8&*/<04DP>&33&A5 >>>>>>>>>>>>>>>>D2H&'()&*(+,DP>D2?(0')DP>D1&44O+3)D >>>>>>>>>>>>66E >>>>>>>>77>J&44>*<0>0.*3&>3:'04>*+>=0>H&'()&*0)>O(*<>*<0>8+)0' >>>>>>>>B:403FG4&H05B0.*3&$3:'046E >>>>I >>>>/&*/<>5!"#$%&'()&*(+,$-./01*(+,>B06 >>>> >>>>>>>>B033+34>C>B0FG033+345D8+)0'4D6E >>>>I I Because the validation object was passed as a parameter to the model, any errors found in that check will be namespaced into a sub-array called $0.*03,&'. The array of errors would look something like this: &33&A5 >>>>D:403,&80D>>CG>DL<(4>?(0')>/&,,+*>=0>081*A;DP >>>>D$0.*03,&'D>CG>&33&A5 >>>>>>>>D1&44O+3)$/+,?(38D>CG>DL<0>H&':04>A+:>0,*030)>(,>*<0>1&44O+3)>?(0')4>)()>,+*>8&*/<;DP >>>>6P !" This ensures that errors from multiple validation objects and models will never overwrite each other. The power of the ORM_Validation_Exception can be leveraged in many different ways to merge errors from related models. Take a look at the list of Examples for some great use cases. Filters Filters in ORM work much like they used to when they were part of the Validate class in 3.0.x however they have been modified to match the flexible syntax of Validation rules in 3.1.x. Filters run as soon as the field is set in your model and should be used to format the data before it is inserted into the Database. Define your filters the same way you define rules, as an array returned by the #$%&&'()*+,-.! method like the following: /01)(23'042*(543'()*+,-.! 6 3333,+*0,437,,78. 3333333390-+,47:+93;<37,,78. 3333333333337,,78.9*,(:9!= 33333333!= 333333339/7-->5,?93;<37,,78. 3333333333337,,78.7,,78.*A(-=39A7-AB/7-->5,?9!!= 33333333!= 3333333392,+7*+?B5493;<37,,78. 3333333333337,,78.9C5,:7*&&?7*+9=37,,78.9&D7)0+9=39EF:F?3G&(&-9!!= 33333333!= 3333!" H When defining filters, you may use the parameters &D7)0+, &'(+)?, and &:5?+) to refer to the field value, field name, and the model instance respectively. Examples Simple: Basic, one table model examples. Validation: Full example of creating a user account and handling validation errors. TODO: The following is a sample list of examples that might be useful. Don't feel limited by this list, or consider these required. Items on the list can be combined, split up, removed or added to. All contribution are appreciated. Examples of changing things like $_table_name, $_labels, with, etc. Example of a one to one relationship. Example of one to many Example of many to many. Validation Example This example will create user accounts and demonstrate how to handle model and controller validation. We will create a form, process it, and display any errors to the user. We will be assuming that the Model_User class contains a method called A7-AB/7-->5,? that is used to turn the plaintext passwords into some kind of hash. The implementation of the hashing methods are beyond the scope of this example and should be provided with the Authentication library you decide to use. SQL schema I$JKLJ3LKMNJ3OC3P#L3JQORLR3S:+:1+,-S3. 33S(?S3(4*.TU!304-(V4+?3P#L3PWNN3KWL#BOPI$J%JPL= 33S0-+,47:+S3D7,2A7,.XY!3P#L3PWNN= !!"#$%%&'()"!*$(+,$(-.//0!123!14556 !!789:;8<!=><!-"?)"0 0!>191>A9BB'CD!!C>E;453!FG;8H>3AIJKL!;432M91F8>:>13A.N Model OP#,#!)QK?BQ)-RH<H7;3GR0!'(!)?Q-R1'!)?(Q+J!$++Q%%!$SS'&Q)TR0N +S$%%!:')QSM:QUVQ(!QWJQB)%!28:!X !!!!#IVS?+!KIB+J?'B!(ISQ%-0 !!!!X !!!!!!!!(QJI(B!$(($Y- !!!!!!!!!!!!RI%Q(B$UQR!AZ!$(($Y- !!!!!!!!!!!!!!!!$(($Y-RB'JMQU#JYR06 !!!!!!!!!!!!!!!!$(($Y-RU?BMSQB[J,R6!$(($Y-R\*$SIQR6!]006 !!!!!!!!!!!!!!!!$(($Y-RU$WMSQB[J,R6!$(($Y-R\*$SIQR6!^_006 !!!!!!!!!!!!!!!!$(($Y-$(($Y-`J,?%6!RI%Q(B$UQM$*$?S$VSQR006 !!!!!!!!!!!!06 !!!!!!!!!!!!R#$%%&'()R!AZ!$(($Y- !!!!!!!!!!!!!!!!$(($Y-RB'JMQU#JYR06 !!!!!!!!!!!!06 !!!!!!!!0N !!!!a !!!!#IVS?+!KIB+J?'B!K?SJQ(%-0 !!!!X !!!!!!!!(QJI(B!$(($Y- !!!!!!!!!!!!R#$%%&'()R!AZ!$(($Y- !!!!!!!!!!!!!!!!$(($Y-$(($Y-`J,?%6!R,$%,M#$%%&'()R006 !!!!!!!!!!!!06 !!!!!!!!0N !!!!a !!!!#IVS?+!KIB+J?'B!I%Q(B$UQM$*$?S$VSQ-`I%Q(B$UQ0 !!!!X !!!!!!!!bb!3,Q(Q!$(Q!%?U#SQ(!&$Y%!J'!)'!J,?%6!VIJ!9!&?SS!I%Q!28:!K'(!J,Q!%$cQ!'K!J,Q!QW$U#SQ !!!!!!!!(QJI(B!28:\\K$+J'(Y-RUQUVQ(R6!$(($Y-RI%Q(B$UQR!AZ!`I%Q(B$UQ00dZS'$)Q)-0N !!!!a !!!!#IVS?+!KIB+J?'B!,$%,M#$%%&'()-`#$%%&'()0 !!!!X !!!!!!!!bb!C'!%'UQJ,?B[!J'!,$%,!J,Q!#$%%&'() !!!!a a HTML Form Please forgive my slightly ugly form. I am trying not to use any modules or unrelated magic. :) OK'(U!$+J?'BAeOPA!485\\%?JQ-RbUQUVQ(%R0N!PZe!UQJ,')Ae#'%Je!$++Q#Jd+,$(%QJAeIJKdLeZ !!!!OS$VQS!K'(AeI%Q(B$UQeZ4%Q(B$UQ\ObS$VQSZ !!!!O?B#IJ!?)AeI%Q(B$UQe!JY#QAeJQWJe!B$UQAeI%Q(B$UQe!*$SIQAeOPA!;((\\[QJ-`*$SIQ%6!RI%Q(B$UQR0N!PZe!bZ !!!!OS$VQS!K'(AeI%Q(B$UQe!+S$%%AeQ(('(eZOPA!;((\\[QJ-`Q(('(%6!RI%Q(B$UQR0N!PZ !!!!OS$VQS!K'(Ae#$%%&'()eZ7$%%&'()\ObS$VQSZ !!!!O?B#IJ!?)Ae#$%%&'()e!JY#QAe#$%%&'()e!B$UQAe#$%%&'()e!*$SIQAeOPA!;((\\[QJ-`*$SIQ%6!R#$%%&'()R0N!PZ !!!!OS$VQS!K'(Ae#$%%&'()e!+S$%%AeQ(('(eZOPA!;((\\[QJ-`Q(('(%6!R#$%%&'()R0N!PZ !!!!OS$VQS!K'(Ae#$%%&'()M+'BK?(UeZ8Q#Q$J!7$%%&'()\ObS$VQSZ !!!!O?B#IJ!?)Ae#$%%&'()M+'BK?(Ue!JY#QAe#$%%&'()e!B$UQAeMQWJQ(B$Sf#$%%&'()M+'BK?(Uge!*$SIQAeOPA!;((\\# !!!!OS$VQS!K'(Ae#$%%&'()M+'BK?(Ue!+S$%%AeQ(('(eZOPA!;((\\#$J,-`Q(('(%6!RMQWJQ(B$ST#$%%&'()M+'BK?(UR0N !!!!OVIJJ'B!JY#QAe%IVU?JeZF(Q$JQObVIJJ'BZ !"#$%&' Controller Remember that the ()**+$%, will be hashed as soon as it is set in the model, for this reason, it is impossible to validate it's length or the fact that it matches the ()**+$%,-.$/#0%& field. The model should not care about validating the ()**+$%,-.$/#0%& field, so we add that logic to the controller and simply ask the model to bundle the errors into one tidy array. Read the filters section to understand how those work. (1230.4#1/.50$/4).50$/-.%6)5678 9 4444:;06+4<4=06+>>#).5$%?7&6&26%*".%6)568 44444444A'*657;)316*B4:-CDEF8 44444444A'20/,76%%$%*B4:6%%$%*8G 44440#47:-CDEF8 44449 44444444:&6&26%4<4DHI>>#).5$%?7&6&26%8 444444444444""4FJ64DHI>>;)316*784&65J$,40*4)4*J$%5.1545$4)**0K/4&)/?4;)316*4)54$/.6 444444444444A';)316*7:-CDEFB4)%%)?71*6%/)&6B4()**+$%,88G 44444444:6L56%/)3-;)316*4<4)%%)?7 444444444444""4FJ641/J)*J6,4()**+$%,40*4/66,6,4#$%4.$&()%0/K45$45J64()**+$%,-.$/#0%&4#063, 444444444444()**+$%,4<'4M%%>>K657:-CDEFB4()**+$%,8B 44444444""4M,,4)3346L56%/)34;)316* 4444444484N4M%%>>K657:-CDEFB4-6L56%/)3B4)%%)?788G 44444444:6L5%)4<4=)30,)50$/>>#).5$%?7:6L56%/)3-;)316*8 444444444444A'%1367()**+$%,-.$/#0%&B4&)5.J6*B4)%%)?7>;)30,)50$/B4>#063,B4()**+$%,88G 444444445%? 444444449 444444444444:&6&26%A'*);67:6L5%)8G 444444444444""4H6,0%6.545J641*6%45$4J0*4()K6 444444444444:5J0*A'%6O16*5A'%6,0%6.57&6&26%*"P:&6&26%A'0,8G 44444444Q 44444444.)5.J47DHI-=)30,)50$/-RL.6(50$/4:68 444444449 444444444444:6%%$%*4<4:6A'6%%$%*7&$,63*8G 44444444Q 4444Q 4444:5J0*A'%6*($/*6A'2$,?7:;06+8G Q Messages application/messages/models/member.php %651%/4)%%)?7 44441*6%/)&64<'4)%%)?7 44444444/$5-6&(5?4<'4S$14&1*54(%$;0,64)41*6%/)&6PB 44444444&0/-36/K5J4<'4FJ641*6%/)&64&1*54264)5436)*54>()%)&T4.J)%).56%*43$/KPB 44444444&)L-36/K5J4<'4FJ641*6%/)&64&1*5426436**45J)/4>()%)&T4.J)%).56%*43$/KPB 444444441*6%/)&6-);)03)2364<'4FJ0*41*6%/)&640*4/$54);)03)236PB 44448B 4444()**+$%,4<'4)%%)?7 44444444/$5-6&(5?4<'4S$14&1*54(%$;0,64)4()**+$%,PB 44448B 8G application/messages/models/member/_external.php %651%/4)%%)?7 4444()**+$%,-.$/#0%&4<'4)%%)?7 !!!!!!!!"#$%&'()"!*+!",'(!-$))./01!23(41)!131!5/%!#$%&'6"7 !!!!87 89 Upgrading Table aliases ORM will now alias the main table in a query to the model's singular object name. i.e. Prior to 3.2 ORM set the from table like so: :%'3);+<1=<=>341(0;+20/#?:%'3);+<%$=4(<5$#(89 As of 3.2 it is now aliased like so: :%'3);+<1=<=>341(0;+20/#?$00$?:%'3);+<%$=4(<5$#(7!:%'3);+</=A(&%<5$#(889 If you have a model B/1(4<C01(0 then when building a query use the alias like so: :#/1(4;+.'(0(?"/01(0631"7!"*"7!:3189 Userguide The userguide module provides documentation viewing including browsing the source code comments. Give tips on how to most effectively use the user guide. How to navigate around, how things are organized, etc. How the Userguide works The userguide uses Markdown for the documentation. Both the userguide pages, and the in code comments for the API browser are written in markdown. Userguide pages Userguide pages are in the module they apply to, in D>31(EF#/1>4(+. For example, documentation for Kohana is in ))%(#ED>31(EG/'$5$ and documentation for orm is in #/1>4()E/0#ED>31(E/0#, database is in #/1>4()E1$%$=$)(ED>31(E1$%$=$)(, etc. Each module has an index page at D>31(EF#/1>4(+E351(H6#1. Each module's menu is at D>31(EF#/1>4(+E#(5>6#1. All other pages are are in D>31(EF#/1>4(+ and can be organized in subfolders and named however you want. For more info on how to make your module have userguide pages, see Adding your module. Images Any images used in the userguide pages must be in #(13$ED>31(EF#/1>4(+E. For example, if a page has IJK#$D( ,3%4(L?'(44/;./0416A-D8 the image would be located at #(13$ED>31(EF#/1>4(+E'(44/;./0416A-D. Images for the ORM module are in #/1>4()E/0#E#(13$ED>31(E/0#, and images for the Kohana docs are in ))%(#E#(13$ED>31(EG/'$5$. API browser The API browser is generated from the actual source code. The descriptions for classes, constants, properties, and methods is extracted from the comments and parsed in Markdown. For example if you look in the comment for Kohana_Core::init you can see a markdown list and table. These are parsed and show correctly in the API browser. M-$0$#, M>)(), M%'0/.), M0(%>05) and other tags are parsed as well. TODO: give more specific details on how to comment your classes, constants, methods, etc. including package and how it relates to the api module. Contributing Kohana is community driven, and we rely on community contributions for the documentation. Guidelines Documentation should use complete sentences, good grammar, and be as clear as possible. Use lots of example code, but make sure the examples follow the Kohana conventions and style. Try to commit often, with each commit only changing a file or two, rather than changing a ton of files and commiting it all at once. This will make it easier to offer feedback and merge your changes. Make sure your commit messages are clear and descriptive. Bad: "Added docs", Good: "Added initial draft of hello world tutorial", Bad: "Fixed typos", Good: "Fixed typos on the query builder page" If you feel a menu needs to be rearranged or a module needs new pages, please open a bug report to discuss it. Quick Method To quickly point out something that needs improvement, report a bug report. If you want to contribute some changes, you can do so right from your browser without even knowing git! First create an account on Github. You will need to fork the module for the area you want to improve. For example, to improve the ORM documentation fork http://github.com/kohana/orm. To improve the Kohana documentation, fork http://github.com/kohana/core, etc. So, find the module you want to improve and click on the Fork button in the top right. The files that make the User Guide portion are found in !"#$%&'()$"*%+&, and the API browser portion is made from the comments in the source code itself. Navigate to one of the files you want to change and click the edit button in the top right of the file viewer. Make the changes and add a detailed commit message. Repeat this for as many files as you want to improve. (Note that you can't preview what the changes will look unless you actually test it locally.) After you have made your changes, send a pull request so your improvements can be reviewed to be merged into the official documentation. Once your pull request has been accepted, you can delete your repository if you want. Your commit will have been copied to the official branch. If you know git Short version: Fork the module whose docs you wish to improve (e.g. !#,-&&!#,."/01)(&2).343&)5(0!#, or !#,-&&!#,."/01)(&2).343&1)5%0!#,), checkout the 607&$%8%*)9 branch (for the 3.2 docs!), make changes, and then send a pull request. Long version: (This still assumes you at least know your way around git, especially how submodules work.) 1. Fork the specific repo you want to contribute to on github. (For example go to http://github.com/kohana/core and click the fork button.) 2. Now you need to add your fork as a "git remote" to your application and ensure you are on the right branch. An example for the ORM module and 3.2 docs: 1$:(;<2).343<399&()$"*%=&)5( >:3$$:;)"5:5%9)=#,)5;:3=:3:4%?:5%(),% !#,:5%(),%:3$$:';)"5:43(%+:!#,-&&!#,."/01)(&';)"5:43(%+&)5(0!#, !"#$%"%&$"'())$'%"*)+,'& -.%"'&$'/(0%"12345$6$7(8 3. Now go into the repo of the area of docs you want to contribute to and add your forked repo as a new remote, and push to it. '5"9:;/(&+,+;+8849(507$<4()9 !"=+/$"<(9$"'&+,-$<"%("%&$"5('< ,+,(">.7$295 !"?(99.%":(0)"'&+,-$<";"<$"+"5$<').8%.6$"'(99.%"9$<<+-$A"B>"%&$)$".<"+")$59.,$"%.'/$%">()"%&$"' -.%"'(99.%";+";9"C?())$'%$5"+"%:8(".,"%&$"GH="5('<2"D.E$<"!I31JK2C !"9+/$"<0)$"L$"+)$"08"%("5+%$"L.%&"%&$"7+%$<%"'&+,-$< -.%"9$)-$"().-.,412345$6$7(8 !"M(L"80<&":(0)"'&+,-$<"%(":(0)">()/2""""""" -.%"80<&"N:(0)",+9$O"12345$6$7(8 4. Finally, Send a pull request on github. Configuration The userguide has the following config options, available in '(,>.-40<$)-0.5$28&8. )$%0),"+))+: P """"44"Q,+*7$"%&$"RSB"*)(L<$)2""THQ"()"DRUVQ """"W+8.X*)(L<$)W""YO"THQZ """"44"Q,+*7$"%&$<$"8+'/+-$<".,"%&$"RSB"*)(L<$)2""THQ">()"+77"8+'/+-$<Z"()"+"<%).,-"(>"'(99+"<$8$)+% """"44"QE+987$\"W+8.X8+'/+-$<W"YO"W](&+,+Z](&+,+4^+%+*+<$Z](&+,+4GH=ZM(,$WZ """"W+8.X8+'/+-$<W"YO"THQZ _` You can enable or disabled the entire api browser, or limit it to only show certain packages. To disable a module from showing pages in the userguide, simply change that modules $,+*7$5 option using the cascading filesystem. For example: a+887.'+%.(,4'(,>.-40<$)-0.5$28&8a )$%0),"+))+: P """"W9(507$<W"YO"+))+: """"P """"""""W/(&+,+W"YO"+))+: """"""""P """"""""""""W$,+*7$5W"YO"DRUVQZ """"""""_Z """"""""W5+%+*+<$W"YO"+))+: """"""""P """"""""""""W$,+*7$5W"YO"DRUVQZ """"""""_ """"_ _ Using this you could make the userguide only show your modules and your classes in the api browser, if you wanted to host your own documentation on your site. Feel free to change the styles and views as well, but be sure to give credit where credit is due! Adding your module to the userguide Making your module work with the userguide is simple. First, copy this config and place in it !"#$%&'()*#+,-.)%/'0.%-$'1232, replacing anything in !( with the appropriate things: 0'4%0+560067 8 5555))59'6:'543-/56&#+' 5555;"#$%&'/;5<(5600678 55555555))5=3-/5/3#%&$5>'543'5264354#543-/5"#$%&'/5%/'0.%-$'526.'/?5-43#%4543'5;.%-$');15ABC5;).%-$' 55555555;!"#$%&'+6"'(;5<(5600678 555555555555))5D3'43'0543-/5"#$%&'/5%/'0.%-$'526.'/5/3#%&$5>'5/3#+ 555555555555;'+6>&'$;5<(5=EFA? 555555555555))5=3'5+6"'543645/3#%&$5/3#5%25#+543'5%/'0.%-$'5-+$'B526.' 555555555555;+6"';5<(5;!G#$%&'5H6"'(;? 555555555555))5I5/3#045$'/*0-24-#+5#,543-/5"#$%&'?5/3#+5#+543'5-+$'B526.' 555555555555;$'/*0-24-#+;5<(5;!J'/*0-24-#+5.#'/53'0'1(;? 555555555555))5K#270-.345"'//6.'?5/3#+5-+543'5,##4'05,#0543-/5"#$%&' 555555555555;*#270-.34;5<(5;L*#27M5NOPOQRSNOPP5!T#%05H6"'(;? 55555555U555 5555U UM Next, create a folder in your module directory called .%-$')!"#$%&'+6"'( and create -+$'B1"$ and "'+%1"$. All userguide pages use Markdown. The index page is what is shown on the index of your module, the menu is what shows up in the side column. The menu should be formatted like this: VV5WG#$%&'5H6"'X8U 5Y5WZ6.'5+6"'X826.'Y2643U 5Y5W=3-/5-/565K64'.#07X8*64'.#07U 55555Y5W[%>5Z6.'X8*64'.#07)/%>Y26.'U 55555Y5WI+#43'0X8*64'.#07)6+#43'0U 555555555Y5W[%>5/%>526.'X8*64'.#07)6+#43'0)/%>Y26.'U 5Y5K64'.#0-'/5$#5+#4536:'54#5>'565&-+\54#56526.' 55555Y5WA4*'4'06X8'4*U Page paths are relative to .%-$')!"#$%&'+6"'(. So WZ6.'5+6"'X82643Y2643U would look for .%-$')!"#$%&'+6"'()26.'Y+6"'1"$ and WI+#43'0X8*64'.#07)6+#43'0U would look for .%-$')!"#$%&'+6"'()26.'Y+6"'1"$. The guide pages can be named or arranged any way you want within that folder (with the exception of "'+%1"$ and -+$'B1"$). The breadcrumbs and page titles are pulled from the "'+%1"$ ,-&', not the file names or paths. You can have items that are not pages (a category that doesn't have a corresponding page). To link to the -+$'B1"$ page, you should have an empty link, e.g. WG#$%&'5H6"'X8U. Do not include 1"$ in your links. Window size: 1044 x 774 Viewport size: 1044 x 635

PARTAGER SUR

Envoyer le lien par email
7550
READS
3
DOWN
0
FOLLOW
6
EMBED
DOCUMENT # TAGS
#kohana  #php framework  #php mvc 

licence non indique


DOCUMENT # INDEX
PHP 
img

Partagé par  Aguirre

 Suivre

Auteur:
Source:Non communique