Merge remote-tracking branch 'upstream/develop' into 4.3

Conflicts:
	system/Helpers/html_helper.php
	tests/system/Helpers/HTMLHelperTest.php
	user_guide_src/source/installation/upgrade_4xx.rst
	user_guide_src/source/installation/upgrade_validations/002.php
This commit is contained in:
kenjis 2022-12-28 08:45:19 +09:00
commit 337de208f8
No known key found for this signature in database
GPG Key ID: BD254878922AF198
23 changed files with 212 additions and 77 deletions

View File

@ -44,7 +44,7 @@ trait RequestTrait
/**
* Gets the user's IP address.
*
* @return string IP address if it can be detected, or empty string.
* @return string IP address if it can be detected.
* If the IP address is not a valid IP address,
* then will return '0.0.0.0'.
*/
@ -73,6 +73,11 @@ trait RequestTrait
$this->ipAddress = $this->getServer('REMOTE_ADDR');
// If this is a CLI request, $this->ipAddress is null.
if ($this->ipAddress === null) {
return $this->ipAddress = '0.0.0.0';
}
if ($proxyIPs) {
// @TODO Extract all this IP address logic to another class.
foreach ($proxyIPs as $proxyIP => $header) {
@ -151,7 +156,7 @@ trait RequestTrait
return $this->ipAddress = '0.0.0.0';
}
return empty($this->ipAddress) ? '' : $this->ipAddress;
return $this->ipAddress;
}
/**

View File

@ -222,13 +222,20 @@ if (! function_exists('link_tag')) {
/**
* Link
*
* Generates link to a CSS file
* Generates link tag
*
* @param array|string $href Stylesheet href or an array
* @param bool $indexPage should indexPage be added to the CSS path.
* @param array<string, bool|string>|string $href Stylesheet href or an array
* @param bool $indexPage should indexPage be added to the CSS path.
*/
function link_tag($href = '', string $rel = 'stylesheet', string $type = 'text/css', string $title = '', string $media = '', bool $indexPage = false, string $hreflang = ''): string
{
function link_tag(
$href = '',
string $rel = 'stylesheet',
string $type = 'text/css',
string $title = '',
string $media = '',
bool $indexPage = false,
string $hreflang = ''
): string {
// extract fields if needed
if (is_array($href)) {
$rel = $href['rel'] ?? $rel;
@ -252,7 +259,7 @@ if (! function_exists('link_tag')) {
$attributes['rel'] = $rel;
if (! in_array($rel, ['alternate', 'canonical'], true)) {
if ($type !== '' && $rel !== 'canonical' && $hreflang === '' && ! ($rel === 'alternate' && $media !== '')) {
$attributes['type'] = $type;
}

View File

@ -365,11 +365,71 @@ final class HTMLHelperTest extends CIUnitTestCase
$doctypes->html5 = $default;
}
public function testLinkTagComplete()
public function testLinkTagMedia()
{
$target = 'https://styles.com/css/mystyles.css';
$expected = '<link href="https://styles.com/css/mystyles.css" rel="banana" type="fruit" media="VHS" title="Go away">';
$this->assertSame($expected, link_tag($target, 'banana', 'fruit', 'Go away', 'VHS'));
$target = 'https://styles.com/css/mystyles.css';
$tag = link_tag($target, 'stylesheet', 'text/css', '', 'print');
$expected = '<link href="https://styles.com/css/mystyles.css" rel="stylesheet" type="text/css" media="print">';
$this->assertSame($expected, $tag);
}
public function testLinkTagTitle()
{
$tag = link_tag('default.css', 'stylesheet', 'text/css', 'Default Style');
$expected = '<link href="http://example.com/default.css" rel="stylesheet" type="text/css" title="Default Style">';
$this->assertSame($expected, $tag);
}
public function testLinkTagFavicon()
{
$tag = link_tag('favicon.ico', 'shortcut icon', 'image/ico');
$expected = '<link href="http://example.com/favicon.ico" rel="shortcut icon" type="image/ico">';
$this->assertSame($expected, $tag);
}
public function testLinkTagRss()
{
$tag = link_tag('feed', 'alternate', 'application/rss+xml', 'My RSS Feed');
$expected = '<link href="http://example.com/feed" rel="alternate" type="application/rss+xml" title="My RSS Feed">';
$this->assertSame($expected, $tag);
}
public function testLinkTagAlternate()
{
$tag = link_tag(
'http://sp.example.com/',
'alternate',
'',
'',
'only screen and (max-width: 640px)'
);
$expected = '<link href="http://sp.example.com/" rel="alternate" media="only screen and (max-width: 640px)">';
$this->assertSame($expected, $tag);
}
public function testLinkTagArrayAlternate()
{
$tag = link_tag([
'href' => 'http://sp.example.com/',
'rel' => 'alternate',
'media' => 'only screen and (max-width: 640px)',
]);
$expected = '<link href="http://sp.example.com/" rel="alternate" media="only screen and (max-width: 640px)">';
$this->assertSame($expected, $tag);
}
public function testLinkTagCanonical()
{
$tag = link_tag('http://www.example.com/', 'canonical');
$expected = '<link href="http://www.example.com/" rel="canonical">';
$this->assertSame($expected, $tag);
}
public function testLinkTagArray()
@ -382,6 +442,18 @@ final class HTMLHelperTest extends CIUnitTestCase
$this->assertSame($expected, link_tag($parms));
}
public function testLinkTagArrayHreflang()
{
$tag = link_tag([
'href' => 'https://example.com/en',
'rel' => 'alternate',
'hreflang' => 'x-default',
]);
$expected = '<link href="https://example.com/en" hreflang="x-default" rel="alternate">';
$this->assertSame($expected, $tag);
}
public function testDocType()
{
$target = 'html4-strict';

View File

@ -16,6 +16,8 @@ A Controller is simply a class file that handles a HTTP request. :doc:`URI Routi
Every controller you create should extend ``BaseController`` class.
This class provides several features that are available to all of your controllers.
.. _controller-constructor:
Constructor
***********

View File

@ -1,17 +1,20 @@
#############
Request Class
*************
#############
The request class is an object-oriented representation of an HTTP request. This is meant to
work for both incoming, such as a request to the application from a browser, and outgoing requests,
like would be used to send a request from the application to a third-party application. This class
like would be used to send a request from the application to a third-party application.
This class
provides the common functionality they both need, but both cases have custom classes that extend
from the Request class to add specific functionality.
from the Request class to add specific functionality. In practice, you will need to use these classes.
See the documentation for the :doc:`IncomingRequest Class </incoming/incomingrequest>` and
:doc:`CURLRequest Class </libraries/curlrequest>` for more usage details.
Class Reference
===============
***************
.. php:namespace:: CodeIgniter\HTTP
@ -19,8 +22,8 @@ Class Reference
.. php:method:: getIPAddress()
:returns: The user's IP Address, if it can be detected, or null. If the IP address
is not a valid IP address, then will return 0.0.0.0
:returns: The user's IP Address, if it can be detected. If the IP address
is not a valid IP address, then will return 0.0.0.0.
:rtype: string
Returns the IP address for the current user. If the IP address is not valid, the method
@ -33,6 +36,9 @@ Class Reference
.. php:method:: isValidIP($ip[, $which = ''])
.. deprecated:: 4.0.5
Use :doc:`../libraries/validation` instead.
.. important:: This method is deprecated. It will be removed in future releases.
:param string $ip: IP address
@ -65,19 +71,30 @@ Class Reference
.. php:method:: setMethod($method)
:param string $upper: Sets the request method. Used when spoofing the request.
:returns: HTTP request method
.. deprecated:: 4.0.5
Use :php:meth:`CodeIgniter\\HTTP\\Request::withMethod()` instead.
:param string $method: Sets the request method. Used when spoofing the request.
:returns: This request
:rtype: Request
.. php:method:: withMethod($method)
.. versionadded:: 4.0.5
:param string $method: Sets the request method.
:returns: New request instance
:rtype: Request
.. php:method:: getServer([$index = null[, $filter = null[, $flags = null]]])
:param mixed $index: Value name
:param int $filter: The type of filter to apply. A list of filters can be found `here <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found `here <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:returns: $_SERVER item value if found, null if not
:param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:returns: ``$_SERVER`` item value if found, null if not
:rtype: mixed
This method is identical to the ``post()``, ``get()`` and ``cookie()`` methods from the
This method is identical to the ``getPost()``, ``getGet()`` and ``getCookie()`` methods from the
:doc:`IncomingRequest Class </incoming/incomingrequest>`, only it fetches server data (``$_SERVER``):
.. literalinclude:: request/004.php
@ -90,13 +107,13 @@ Class Reference
.. php:method:: getEnv([$index = null[, $filter = null[, $flags = null]]])
:param mixed $index: Value name
:param int $filter: The type of filter to apply. A list of filters can be found `here <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found `here <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:returns: $_ENV item value if found, null if not
:param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:returns: ``$_ENV`` item value if found, null if not
:rtype: mixed
This method is identical to the ``post()``, ``get()`` and ``cookie()`` methods from the
:doc:`IncomingRequest Class </incoming/incomingrequest>`, only it fetches getEnv data (``$_ENV``):
This method is identical to the ``getPost()``, ``getGet()`` and ``getCookie()`` methods from the
:doc:`IncomingRequest Class </incoming/incomingrequest>`, only it fetches env data (``$_ENV``):
.. literalinclude:: request/006.php
@ -109,17 +126,17 @@ Class Reference
:param string $method: Method name
:param mixed $value: Data to be added
:returns: HTTP request method
:rtype: Request
:returns: This request
:rtype: Request
Allows manually setting the value of PHP global, like $_GET, $_POST, etc.
Allows manually setting the value of PHP global, like ``$_GET``, ``$_POST``, etc.
.. php:method:: fetchGlobal($method [, $index = null[, $filter = null[, $flags = null]]])
:param string $method: Input filter constant
:param mixed $index: Value name
:param int $filter: The type of filter to apply. A list of filters can be found `here <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found `here <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:param int $filter: The type of filter to apply. A list of filters can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.php>`__.
:param int|array $flags: Flags to apply. A list of flags can be found in `PHP manual <https://www.php.net/manual/en/filter.filters.flags.php>`__.
:rtype: mixed
Fetches one or more items from a global, like cookies, get, post, etc.

View File

@ -1,5 +1,5 @@
<?php
echo $request->getMethod(true); // Outputs: POST
echo $request->getMethod(true); // Outputs: POST
echo $request->getMethod(false); // Outputs: post
echo $request->getMethod(); // Outputs: post
echo $request->getMethod(); // Outputs: post

View File

@ -35,8 +35,8 @@ General Adjustments
Downloads
=========
- CI4 is still available as a ready-to-run zip or tarball.
- It can also be installed using Composer.
- CI4 is still available as a :doc:`ready-to-run zip or tarball <../installation/installing_manual>`.
- It can also be installed using :doc:`Composer <../installation/installing_composer>`.
Namespaces
==========
@ -90,10 +90,10 @@ Class Loading
- There is no longer a CodeIgniter "superobject", with framework component
references magically injected as properties of your controller.
- Classes are instantiated where needed, and components are managed
by ``Services``.
- The class loader automatically handles PSR-4 style class locating,
within the ``App`` (**app**) and ``CodeIgniter`` (i.e., **system**) top level
- Classes are instantiated where needed, and framework components are managed
by :doc:`../concepts/services`.
- The :doc:`Autoloader <../concepts/autoloader>` automatically handles PSR-4 style class locating,
within the ``App`` (**app** folder) and ``CodeIgniter`` (i.e., **system** folder) top level
namespaces; with Composer autoloading support.
- You can configure the class loading to support whatever application structure
you are most comfortable with, including the "HMVC" style.
@ -102,14 +102,32 @@ Libraries
=========
- Your app classes can still go inside **app/Libraries**, but they don't have to.
- Instead of CI3's ``$this->load->library(x);`` you can now use
- Instead of CI3's ``$this->load->library('x');`` you can now use
``$this->x = new X();``, following namespaced conventions for your component.
Helpers
=======
- Helpers are pretty much the same as before, though some have been simplified.
- :doc:`Helpers <../general/helpers>` are pretty much the same as before, though some have been simplified.
- Since v4.3.0, you can autoload helpers by **app/Config/Autoload.php** as well as CI3.
- Some helpers from CodeIgniter 3 no longer exists in Version 4. For all these
helpers, you have to find a new way to implement your functions. These
helpers are `CAPTCHA Helper <https://www.codeigniter.com/userguide3/helpers/captcha_helper.html>`_,
`Email Helper <https://www.codeigniter.com/userguide3/helpers/email_helper.html>`_.
`Path Helper <https://www.codeigniter.com/userguide3/helpers/path_helper.html>`_.
and `Smiley Helper <https://www.codeigniter.com/userguide3/helpers/smiley_helper.html>`_.
- `Download Helper <https://www.codeigniter.com/userguide3/helpers/download_helper.html>`_
in CI3 was removed. You need to use Response object where you are using ``force_download()``.
See :ref:`force-file-download`.
- `Language Helper <https://www.codeigniter.com/userguide3/helpers/language_helper.html>`_
in CI3 was removed. But ``lang()`` is always available in CI4. See :php:func:`lang()`.
- `Typography Helper <https://www.codeigniter.com/userguide3/helpers/typography_helper.html>`_
in CI3 wll be :doc:`Typography Library <../libraries/typography>` in CI4.
- `Directory Helper <https://www.codeigniter.com/userguide3/helpers/directory_helper.html>`_
and `File Helper <https://www.codeigniter.com/userguide3/helpers/file_helper.html>`_ in CI3
will be :doc:`../helpers/filesystem_helper` in CI4.
- `String Helper <https://www.codeigniter.com/userguide3/helpers/string_helper.html>`_ functions
in CI3 are included in :doc:`../helpers/text_helper` in CI4.
- In CI4, ``redirect()`` returns a ``RedirectResponse`` instance instead of redirecting and terminating script execution. You must return it.
- `redirect() Documentation CodeIgniter 3.X <https://codeigniter.com/userguide3/helpers/url_helper.html#redirect>`_
- `redirect() Documentation CodeIgniter 4.X <../general/common_functions.html#redirect>`_
@ -117,7 +135,8 @@ Helpers
Events
======
- Hooks have been replaced by Events.
- `Hooks <https://www.codeigniter.com/userguide3/general/hooks.html>`_ have been
replaced by :doc:`../extending/events`.
- Instead of CI3's ``$hook['post_controller_constructor']`` you now use
``Events::on('post_controller_constructor', ['MyClass', 'MyFunction']);``, with the namespace ``CodeIgniter\Events\Events;``.
- Events are always enabled, and are available globally.
@ -127,17 +146,18 @@ Extending the Framework
- You don't need a **core** folder to hold ``MY_...`` framework
component extensions or replacements.
- You don't need ``MY_x`` classes inside your libraries folder
- You don't need ``MY_X`` classes inside your libraries folder
to extend or replace CI4 pieces.
- Make any such classes where you like, and add appropriate
service methods in **app/Config/Services.php** to load
your components instead of the default ones.
- See :doc:`../extending/core_classes` for details.
Upgrading Libraries
*******************
- Your app classes can still go inside **app/Libraries**, but they don't have to.
- Instead of CI3's ``$this->load->library(x);`` you can now use ``$this->x = new X();``,
- Instead of CI3's ``$this->load->library('x');`` you can now use ``$this->x = new X();``,
following namespaced conventions for your component.
- Some libraries from CodeIgniter 3 no longer exists in Version 4. For all these
libraries, you have to find a new way to implement your functions. These

View File

@ -15,6 +15,8 @@ What has been changed
=====================
- In CI4, the configurations are now stored in classes which extend ``CodeIgniter\Config\BaseConfig``.
- The **application/config/config.php** in CI3 will be **app/Config/App.php**
and some other files like **app/Config/Security.php** for the specific classes.
- Within the configuration class, the config values are stored in public class properties.
- The method to fetch config values has been changed.
@ -30,7 +32,7 @@ Upgrade Guide
from the CI3 config into the new CI4 config class as public class properties.
4. Now, you have to change the config fetching syntax everywhere you fetch config
values. The CI3 syntax is something like ``$this->config->item('item_name');``.
You have to change this into ``config('MyConfigFile')->item_name;``.
You have to change this into ``config('MyConfig')->item_name;``.
Code Example
============
@ -38,13 +40,13 @@ Code Example
CodeIgniter Version 3.x
------------------------
Path: **application/config**:
Path: **application/config/site.php**:
.. literalinclude:: upgrade_configuration/ci3sample/001.php
CodeIgniter Version 4.x
-----------------------
Path: **app/Config**:
Path: **app/Config/Site.php**:
.. literalinclude:: upgrade_configuration/001.php

View File

@ -4,7 +4,7 @@ namespace Config;
use CodeIgniter\Config\BaseConfig;
class CustomClass extends BaseConfig
class Site extends BaseConfig
{
public $siteName = 'My Great Site';
public $siteEmail = 'webmaster@example.com';

View File

@ -15,10 +15,11 @@ What has been changed
=====================
- Since namespaces have been added to CodeIgniter 4, the controllers must be changed to support namespaces.
- Controllers don't use constructors any more (to invoke CI 'magic') unless those are part of base controllers you make.
- CI provides Request and Response objects for you to work with - more powerful than the CI3-way.
- If you want a base controller (``MY_Controller`` in CI3), make it where you like,
e.g., BaseController extends Controller, and then have your controllers extend it
- The constructor of CI4 Controller does not automatically load core classes into the properties.
- CI4's Controller has a special constructor :ref:`initController() <controller-constructor>`.
- CI4 provides :doc:`Request </incoming/incomingrequest>` and :doc:`Responses </outgoing/response>`
objects for you to work with - more powerful than the CI3-way.
- If you want a base controller (``MY_Controller`` in CI3), use **app/Controllers/BaseController.php**.
Upgrade Guide
=============

View File

@ -4,6 +4,6 @@ class Helloworld extends CI_Controller
{
public function index($name)
{
echo "Hello $name! ";
echo 'Hello ' . html_escape($name) . '!';
}
}

View File

@ -14,7 +14,8 @@ Documentations
What has been changed
=====================
- The functionality in CI3 is basically the same as in CI4.
- The method names have changed to camelCase and the query builder now needs to be initialized before you can run queries on it.
- The method names have changed to camelCase and the :doc:`Query Builder <../database/query_builder>`
now needs to be initialized before you can run queries on it.
Upgrade Guide
=============

View File

@ -12,9 +12,13 @@ class Upload extends BaseController
public function do_upload()
{
$this->validate([
'userfile' => 'uploaded[userfile]|max_size[userfile,100]'
. '|mime_in[userfile,image/png,image/jpg,image/gif]'
. '|ext_in[userfile,png,jpg,gif]|max_dims[userfile,1024,768]',
'userfile' => [
'uploaded[userfile]',
'max_size[userfile,100]',
'mime_in[userfile,image/png,image/jpg,image/gif]',
'ext_in[userfile,png,jpg,gif]',
'max_dims[userfile,1024,768]',
],
]);
$file = $this->request->getFile('userfile');

View File

@ -24,7 +24,7 @@ Upgrade Guide
2. Add this line just after the opening php tag: ``namespace App\Models;``.
3. Below the ``namespace App\Models;`` line add this line: ``use CodeIgniter\Model;``.
4. Replace ``extends CI_Model`` with ``extends Model``.
5. Instead of CI3's ``$this->load->model(x);``, you would now use ``$this->x = new X();``, following namespaced conventions for your component. Alternatively, you can use the :php:func:`model()` function: ``$this->x = model('X');``.
5. Instead of CI3's ``$this->load->model('x');``, you would now use ``$this->x = new X();``, following namespaced conventions for your component. Alternatively, you can use the :php:func:`model()` function: ``$this->x = model('X');``.
If you use sub-directories in your model structure you have to change the namespace according to that.
Example: You have a version 3 model located in **application/models/users/user_contact.php** the namespace has to be ``namespace App\Models\Users;`` and the model path in the version 4 should look like this: **app/Models/Users/UserContact.php**
@ -32,7 +32,7 @@ Example: You have a version 3 model located in **application/models/users/user_c
The new Model in CI4 has a lot of built-in methods. For example, the ``find($id)`` method. With this you can find data where the primary key is equal to ``$id``.
Inserting data is also easier than before. In CI4 there is an ``insert($data)`` method. You can optionally make use of all those built-in methods and migrate your code to the new methods.
You can find more information to those methods :doc:`here </models/model>`.
You can find more information to those methods in :doc:`../models/model`.
Code Example
============

View File

@ -17,7 +17,7 @@ What has been changed
Upgrade Guide
=============
1. The methods in the HTTP Responses class are named slightly different. The most important change in the naming is the switch from underscored method names to camelCase. The method ``set_content_type()`` from version 3 is now named ``setContentType()`` and so on.
2. In the most cases you have to change ``$this->output`` to ``$this->response`` followed by the method. You can find all methods :doc:`here </outgoing/response>`.
2. In the most cases you have to change ``$this->output`` to ``$this->response`` followed by the method. You can find all methods in :doc:`../outgoing/response`.
Code Example
============

View File

@ -12,11 +12,11 @@ Documentations
- :doc:`Security Documentation CodeIgniter 4.X </libraries/security>`
.. note::
If you use the :doc:`form helper </helpers/form_helper>` and enable the CSRF filter globally, then ``form_open()`` will automatically insert a hidden CSRF field in your forms. So you do not have to upgrade this by yourself.
If you use the :doc:`../helpers/form_helper` and enable the CSRF filter globally, then :php:func:`form_open()` will automatically insert a hidden CSRF field in your forms. So you do not have to upgrade this by yourself.
What has been changed
=====================
- The method to implement CSRF tokens to html forms has been changed.
- The method to implement CSRF tokens to HTML forms has been changed.
Upgrade Guide
=============

View File

@ -14,5 +14,6 @@ class Filters extends BaseConfig
'csrf',
],
];
// ...
}

View File

@ -2,7 +2,7 @@
$session = session();
$_SESSION['item'];
$_SESSION['item']; // But we do not recommend to use superglobal directly.
$session->get('item');
$session->item;
session('item');

View File

@ -13,7 +13,7 @@ Documentations of Library
What has been changed
=====================
- If you want to change validation error display, you have to set CI4 validation View templates.
- If you want to change validation error display, you have to set CI4 :ref:`validation View templates <validation-customizing-error-display>`.
- CI4 validation has no Callbacks nor Callable in CI3.
- CI4 validation format rules do not permit empty string.
- CI4 validation never changes your data.
@ -30,7 +30,7 @@ Upgrade Guide
- ``$this->load->helper(array('form', 'url'));`` to ``helper(['form', 'url']);``
- remove the line ``$this->load->library('form_validation');``
- ``if ($this->form_validation->run() == FALSE)`` to ``if (! $this->validate([]))``
- ``$this->load->view('myform');`` to ``echo view('myform', ['validation' => $this->validator,]);``
- ``$this->load->view('myform');`` to ``return view('myform', ['validation' => $this->validator,]);``
3. You have to change the validation rules. The new syntax is to set the rules as array in the controller:

View File

@ -13,9 +13,9 @@ class Form extends Controller
if (! $this->validate([
// Validation rules
])) {
echo view('myform');
} else {
echo view('formsuccess');
return view('myform');
}
return view('formsuccess');
}
}

View File

@ -15,9 +15,10 @@ What has been changed
=====================
- Your views look much like before, but they are invoked differently ... instead of CI3's
``$this->load->view(x);``, you can use ``return view(x);``.
- CI4 supports *View Cells* to build your response in pieces, and *View Layouts* for page layout.
- The template parser is still there, and substantially enhanced.
``$this->load->view('x');``, you can use ``return view('x');``.
- CI4 supports :doc:`../outgoing/view_cells` to build your response in pieces,
and :doc:`../outgoing/view_layouts` for page layout.
- The :doc:`Template Parser <../outgoing/view_parser>` is still there, and substantially enhanced.
Upgrade Guide
=============

View File

@ -1,15 +1,15 @@
<html>
<head>
<title><?php echo $title; ?></title>
<title><?php echo html_escape($title); ?></title>
</head>
<body>
<h1><?php echo $heading; ?></h1>
<h1><?php echo html_escape($heading); ?></h1>
<h3>My Todo List</h3>
<ul>
<?php foreach ($todo_list as $item): ?>
<li><?php echo $item; ?></li>
<li><?php echo html_escape($item); ?></li>
<?php endforeach; ?>
</ul>

View File

@ -59,6 +59,8 @@ parameter. This is not case-sensitive.
.. literalinclude:: response/006.php
.. _force-file-download:
Force File Download
===================