This commit is contained in:
2020-10-06 14:27:47 +07:00
commit 586be80cf6
16613 changed files with 3274099 additions and 0 deletions

View File

@@ -0,0 +1,399 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
if (version_compare(PHP_VERSION, '4.3', '<')) {
echo 'At least PHP 4.3 is required to run this script!';
exit(1);
}
/**
* YiiRequirementChecker allows checking, if current system meets the requirements for running the Yii application.
* This class allows rendering of the check report for the web and console application interface.
*
* Example:
*
* ```php
* require_once 'path/to/YiiRequirementChecker.php';
* $requirementsChecker = new YiiRequirementChecker();
* $requirements = array(
* array(
* 'name' => 'PHP Some Extension',
* 'mandatory' => true,
* 'condition' => extension_loaded('some_extension'),
* 'by' => 'Some application feature',
* 'memo' => 'PHP extension "some_extension" required',
* ),
* );
* $requirementsChecker->checkYii()->check($requirements)->render();
* ```
*
* If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]]
*
* Requirement condition could be in format "eval:PHP expression".
* In this case specified PHP expression will be evaluated in the context of this class instance.
* For example:
*
* ```php
* $requirements = array(
* array(
* 'name' => 'Upload max file size',
* 'condition' => 'eval:$this->checkUploadMaxFileSize("5M")',
* ),
* );
* ```
*
* Note: this class definition does not match ordinary Yii style, because it should match PHP 4.3
* and should not use features from newer PHP versions!
*
* @property array|null $result the check results, this property is for internal usage only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class YiiRequirementChecker
{
/**
* Check the given requirements, collecting results into internal field.
* This method can be invoked several times checking different requirement sets.
* Use [[getResult()]] or [[render()]] to get the results.
* @param array|string $requirements requirements to be checked.
* If an array, it is treated as the set of requirements;
* If a string, it is treated as the path of the file, which contains the requirements;
* @return $this self instance.
*/
function check($requirements)
{
if (is_string($requirements)) {
$requirements = require $requirements;
}
if (!is_array($requirements)) {
$this->usageError('Requirements must be an array, "' . gettype($requirements) . '" has been given!');
}
if (!isset($this->result) || !is_array($this->result)) {
$this->result = array(
'summary' => array(
'total' => 0,
'errors' => 0,
'warnings' => 0,
),
'requirements' => array(),
);
}
foreach ($requirements as $key => $rawRequirement) {
$requirement = $this->normalizeRequirement($rawRequirement, $key);
$this->result['summary']['total']++;
if (!$requirement['condition']) {
if ($requirement['mandatory']) {
$requirement['error'] = true;
$requirement['warning'] = true;
$this->result['summary']['errors']++;
} else {
$requirement['error'] = false;
$requirement['warning'] = true;
$this->result['summary']['warnings']++;
}
} else {
$requirement['error'] = false;
$requirement['warning'] = false;
}
$this->result['requirements'][] = $requirement;
}
return $this;
}
/**
* Performs the check for the Yii core requirements.
* @return YiiRequirementChecker self instance.
*/
function checkYii()
{
return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php');
}
/**
* Return the check results.
* @return array|null check results in format:
*
* ```php
* array(
* 'summary' => array(
* 'total' => total number of checks,
* 'errors' => number of errors,
* 'warnings' => number of warnings,
* ),
* 'requirements' => array(
* array(
* ...
* 'error' => is there an error,
* 'warning' => is there a warning,
* ),
* ...
* ),
* )
* ```
*/
function getResult()
{
if (isset($this->result)) {
return $this->result;
} else {
return null;
}
}
/**
* Renders the requirements check result.
* The output will vary depending is a script running from web or from console.
*/
function render()
{
if (!isset($this->result)) {
$this->usageError('Nothing to render!');
}
$baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views';
if (!empty($_SERVER['argv'])) {
$viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php';
} else {
$viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php';
}
$this->renderViewFile($viewFileName, $this->result);
}
/**
* Checks if the given PHP extension is available and its version matches the given one.
* @param string $extensionName PHP extension name.
* @param string $version required PHP extension version.
* @param string $compare comparison operator, by default '>='
* @return bool if PHP extension version matches.
*/
function checkPhpExtensionVersion($extensionName, $version, $compare = '>=')
{
if (!extension_loaded($extensionName)) {
return false;
}
$extensionVersion = phpversion($extensionName);
if (empty($extensionVersion)) {
return false;
}
if (strncasecmp($extensionVersion, 'PECL-', 5) === 0) {
$extensionVersion = substr($extensionVersion, 5);
}
return version_compare($extensionVersion, $version, $compare);
}
/**
* Checks if PHP configuration option (from php.ini) is on.
* @param string $name configuration option name.
* @return bool option is on.
*/
function checkPhpIniOn($name)
{
$value = ini_get($name);
if (empty($value)) {
return false;
}
return ((int) $value === 1 || strtolower($value) === 'on');
}
/**
* Checks if PHP configuration option (from php.ini) is off.
* @param string $name configuration option name.
* @return bool option is off.
*/
function checkPhpIniOff($name)
{
$value = ini_get($name);
if (empty($value)) {
return true;
}
return (strtolower($value) === 'off');
}
/**
* Compare byte sizes of values given in the verbose representation,
* like '5M', '15K' etc.
* @param string $a first value.
* @param string $b second value.
* @param string $compare comparison operator, by default '>='.
* @return bool comparison result.
*/
function compareByteSize($a, $b, $compare = '>=')
{
$compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')';
return $this->evaluateExpression($compareExpression);
}
/**
* Gets the size in bytes from verbose size representation.
* For example: '5K' => 5*1024
* @param string $verboseSize verbose size representation.
* @return int actual size in bytes.
*/
function getByteSize($verboseSize)
{
if (empty($verboseSize)) {
return 0;
}
if (is_numeric($verboseSize)) {
return (int) $verboseSize;
}
$sizeUnit = trim($verboseSize, '0123456789');
$size = trim(str_replace($sizeUnit, '', $verboseSize));
if (!is_numeric($size)) {
return 0;
}
switch (strtolower($sizeUnit)) {
case 'kb':
case 'k':
return $size * 1024;
case 'mb':
case 'm':
return $size * 1024 * 1024;
case 'gb':
case 'g':
return $size * 1024 * 1024 * 1024;
default:
return 0;
}
}
/**
* Checks if upload max file size matches the given range.
* @param string|null $min verbose file size minimum required value, pass null to skip minimum check.
* @param string|null $max verbose file size maximum required value, pass null to skip maximum check.
* @return bool success.
*/
function checkUploadMaxFileSize($min = null, $max = null)
{
$postMaxSize = ini_get('post_max_size');
$uploadMaxFileSize = ini_get('upload_max_filesize');
if ($min !== null) {
$minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>=');
} else {
$minCheckResult = true;
}
if ($max !== null) {
$maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<=');
} else {
$maxCheckResult = true;
}
return ($minCheckResult && $maxCheckResult);
}
/**
* Renders a view file.
* This method includes the view file as a PHP script
* and captures the display result if required.
* @param string $_viewFile_ view file
* @param array $_data_ data to be extracted and made available to the view file
* @param bool $_return_ whether the rendering result should be returned as a string
* @return string the rendering result. Null if the rendering result is not required.
*/
function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false)
{
// we use special variable names here to avoid conflict when extracting data
if (is_array($_data_)) {
extract($_data_, EXTR_PREFIX_SAME, 'data');
} else {
$data = $_data_;
}
if ($_return_) {
ob_start();
ob_implicit_flush(false);
require $_viewFile_;
return ob_get_clean();
} else {
require $_viewFile_;
}
}
/**
* Normalizes requirement ensuring it has correct format.
* @param array $requirement raw requirement.
* @param int $requirementKey requirement key in the list.
* @return array normalized requirement.
*/
function normalizeRequirement($requirement, $requirementKey = 0)
{
if (!is_array($requirement)) {
$this->usageError('Requirement must be an array!');
}
if (!array_key_exists('condition', $requirement)) {
$this->usageError("Requirement '{$requirementKey}' has no condition!");
} else {
$evalPrefix = 'eval:';
if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) {
$expression = substr($requirement['condition'], strlen($evalPrefix));
$requirement['condition'] = $this->evaluateExpression($expression);
}
}
if (!array_key_exists('name', $requirement)) {
$requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey;
}
if (!array_key_exists('mandatory', $requirement)) {
if (array_key_exists('required', $requirement)) {
$requirement['mandatory'] = $requirement['required'];
} else {
$requirement['mandatory'] = false;
}
}
if (!array_key_exists('by', $requirement)) {
$requirement['by'] = 'Unknown';
}
if (!array_key_exists('memo', $requirement)) {
$requirement['memo'] = '';
}
return $requirement;
}
/**
* Displays a usage error.
* This method will then terminate the execution of the current application.
* @param string $message the error message
*/
function usageError($message)
{
echo "Error: $message\n\n";
exit(1);
}
/**
* Evaluates a PHP expression under the context of this class.
* @param string $expression a PHP expression to be evaluated.
* @return mixed the expression result.
*/
function evaluateExpression($expression)
{
return eval('return ' . $expression . ';');
}
/**
* Returns the server information.
* @return string server information.
*/
function getServerInfo()
{
return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
}
/**
* Returns the now date if possible in string representation.
* @return string now date.
*/
function getNowDate()
{
return @strftime('%Y-%m-%d %H:%M', time());
}
}

View File

@@ -0,0 +1,115 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/* @var $this YiiRequirementChecker */
/**
* These are the Yii core requirements for the [[YiiRequirementChecker]] instance.
* These requirements are mandatory for any Yii application.
*/
return array(
array(
'name' => 'PHP version',
'mandatory' => true,
'condition' => version_compare(PHP_VERSION, '5.4.0', '>='),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>',
'memo' => 'PHP 5.4.0 or higher is required.',
),
array(
'name' => 'Reflection extension',
'mandatory' => true,
'condition' => class_exists('Reflection', false),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>',
),
array(
'name' => 'PCRE extension',
'mandatory' => true,
'condition' => extension_loaded('pcre'),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>',
),
array(
'name' => 'SPL extension',
'mandatory' => true,
'condition' => extension_loaded('SPL'),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>',
),
array(
'name' => 'Ctype extension',
'mandatory' => true,
'condition' => extension_loaded('ctype'),
'by' => '<a href="http://www.yiiframework.com">Yii Framework</a>'
),
array(
'name' => 'MBString extension',
'mandatory' => true,
'condition' => extension_loaded('mbstring'),
'by' => '<a href="http://www.php.net/manual/en/book.mbstring.php">Multibyte string</a> processing',
'memo' => 'Required for multibyte encoding string processing.'
),
array(
'name' => 'OpenSSL extension',
'mandatory' => false,
'condition' => extension_loaded('openssl'),
'by' => '<a href="http://www.yiiframework.com/doc-2.0/yii-base-security.html">Security Component</a>',
'memo' => 'Required by encrypt and decrypt methods.'
),
array(
'name' => 'Intl extension',
'mandatory' => false,
'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2', '>='),
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use advanced parameters formatting
in <code>Yii::t()</code>, non-latin languages with <code>Inflector::slug()</code>,
<abbr title="Internationalized domain names">IDN</abbr>-feature of
<code>EmailValidator</code> or <code>UrlValidator</code> or the <code>yii\i18n\Formatter</code> class.'
),
array(
'name' => 'ICU version',
'mandatory' => false,
'condition' => defined('INTL_ICU_VERSION') && version_compare(INTL_ICU_VERSION, '49', '>='),
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'ICU 49.0 or higher is required when you want to use <code>#</code> placeholder in plural rules
(for example, plural in
<a href=\"http://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html#asRelativeTime%28%29-detail\">
Formatter::asRelativeTime()</a>) in the <code>yii\i18n\Formatter</code> class. Your current ICU version is ' .
(defined('INTL_ICU_VERSION') ? INTL_ICU_VERSION : '(ICU is missing)') . '.'
),
array(
'name' => 'ICU Data version',
'mandatory' => false,
'condition' => defined('INTL_ICU_DATA_VERSION') && version_compare(INTL_ICU_DATA_VERSION, '49.1', '>='),
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'ICU Data 49.1 or higher is required when you want to use <code>#</code> placeholder in plural rules
(for example, plural in
<a href=\"http://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html#asRelativeTime%28%29-detail\">
Formatter::asRelativeTime()</a>) in the <code>yii\i18n\Formatter</code> class. Your current ICU Data version is ' .
(defined('INTL_ICU_DATA_VERSION') ? INTL_ICU_DATA_VERSION : '(ICU Data is missing)') . '.'
),
array(
'name' => 'Fileinfo extension',
'mandatory' => false,
'condition' => extension_loaded('fileinfo'),
'by' => '<a href="http://www.php.net/manual/en/book.fileinfo.php">File Information</a>',
'memo' => 'Required for files upload to detect correct file mime-types.'
),
array(
'name' => 'DOM extension',
'mandatory' => false,
'condition' => extension_loaded('dom'),
'by' => '<a href="http://php.net/manual/en/book.dom.php">Document Object Model</a>',
'memo' => 'Required for REST API to send XML responses via <code>yii\web\XmlResponseFormatter</code>.'
),
array(
'name' => 'IPv6 support',
'mandatory' => false,
'condition' => defined('AF_INET6'),
'by' => 'IPv6 expansion in <a href="http://www.yiiframework.com/doc-2.0/yii-validators-ipvalidator.html">IpValidator</a>',
'memo' => 'When <a href="http://www.yiiframework.com/doc-2.0/yii-validators-ipvalidator.html#$expandIPv6-detail">IpValidator::expandIPv6</a>
property is set to <code>true</code>, PHP must support IPv6 protocol stack. Currently PHP constant <code>AF_INET6</code> is not defined
and IPv6 is probably unsupported.'
)
);

View File

@@ -0,0 +1,42 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/* @var $this YiiRequirementChecker */
/* @var $summary array */
/* @var $requirements array[] */
echo "\nYii Application Requirement Checker\n\n";
echo "This script checks if your server configuration meets the requirements\n";
echo "for running Yii application.\n";
echo "It checks if the server is running the right version of PHP,\n";
echo "if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.\n";
$header = 'Check conclusion:';
echo "\n{$header}\n";
echo str_pad('', strlen($header), '-') . "\n\n";
foreach ($requirements as $key => $requirement) {
if ($requirement['condition']) {
echo $requirement['name'] . ": OK\n";
echo "\n";
} else {
echo $requirement['name'] . ': ' . ($requirement['mandatory'] ? 'FAILED!!!' : 'WARNING!!!') . "\n";
echo 'Required by: ' . strip_tags($requirement['by']) . "\n";
$memo = strip_tags($requirement['memo']);
if (!empty($memo)) {
echo 'Memo: ' . strip_tags($requirement['memo']) . "\n";
}
echo "\n";
}
}
$summaryString = 'Errors: ' . $summary['errors'] . ' Warnings: ' . $summary['warnings'] . ' Total checks: ' . $summary['total'];
echo str_pad('', strlen($summaryString), '-') . "\n";
echo $summaryString;
echo "\n\n";

View File

@@ -0,0 +1,63 @@
<style type="text/css">
*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}
header,main,footer{display:block}
a{background-color:transparent;color:#337ab7;text-decoration:none}
a:hover,a:focus{color:#23527c;text-decoration:underline}
a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}
a:active,a:hover{outline:0}
abbr[title]{border-bottom:1px dotted}
abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}
strong{font-weight:700}
h1{font-size:36px;margin:.67em 0}
h3{font-size:24px}
h1,h3{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}
h1,h3{margin-top:20px;margin-bottom:10px}
hr{box-sizing:content-box;height:0;margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}
code{font-family:Menlo,Monaco,Consolas,"Courier New",monospace;padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}
table{border-collapse:collapse;border-spacing:0}
td,th{padding:0}
p{margin:0 0 10px}
.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}
table{background-color:transparent}
th{text-align:left}
.table{width:100%;max-width:100%;margin-bottom:20px}
.table>tbody>tr>th,.table>tbody>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}
.table>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}
.table>tr:first-child>th,.table>tr:first-child>td{border-top:0}
.table>tbody+tbody{border-top:2px solid #ddd}
.table .table{background-color:#fff}
.table-bordered{border:1px solid #ddd}
.table-bordered>tbody>tr>th,.table-bordered>tbody>tr>td{border:1px solid #ddd}
.table-bordered>tr>th,.table-bordered>tr>td{border-bottom-width:2px}
table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}
.table tr>td.active,.table tr>th.active,.table tr.active>td,.table tr.active>th{background-color:#f5f5f5;border-color:#e8e8e8}
.table tr>td.success,.table tr>th.success,.table tr.success>td,.table tr.success>th{background-color:#dff0d8;border-color:#d6e9c6}
.table tr>td.info,.table tr>th.info,.table tr.info>td,.table tr.info>th{background-color:#d9edf7;border-color:#c4ebf3}
.table tr>td.warning,.table tr>th.warning,.table tr.warning>td,.table tr.warning>th{background-color:#fcf8e3;border-color:#faebcc}
.table tr>td.danger,.table tr>th.danger,.table tr.danger>td,.table tr.danger>th{background-color:#f2dede;border-color:#ebccd1}
.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}
.alert>p{margin-bottom:0}.alert>p+p{margin-top:5px}
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}
.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}
.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}
@media (min-width:768px){.container{width:750px}}
@media (min-width:992px){.container{width:970px}}
@media (min-width:1200px){.container{width:1170px}}
@media print{
*,:before,:after{background:transparent!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}
a,a:visited{text-decoration:underline}
a[href]:after{content:" (" attr(href) ")"}
abbr[title]:after{content:" (" attr(title) ")"}
a[href^="#"]:after,a[href^="javascript:"]:after{content:""}
tr{page-break-inside:avoid}
p,h3{orphans:3;widows:3}
h3{page-break-after:avoid}
.table{border-collapse:collapse!important}
.table td,.table th{background-color:#fff!important}
.table-bordered th,.table-bordered td{border:1px solid #ddd!important}
}
</style>

View File

@@ -0,0 +1,77 @@
<?php
/* @var $this YiiRequirementChecker */
/* @var $summary array */
/* @var $requirements array[] */
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Yii Application Requirement Checker</title>
<?php $this->renderViewFile(dirname(__FILE__) . '/css.php'); ?>
</head>
<body>
<div class="container">
<header>
<h1>Yii Application Requirement Checker</h1>
</header>
<hr>
<main>
<h3>Description</h3>
<p>
This script checks if your server configuration meets the requirements
for running Yii application.
It checks if the server is running the right version of PHP,
if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.
</p>
<p>
There are two kinds of requirements being checked. Mandatory requirements are those that have to be met
to allow Yii to work as expected. There are also some optional requirements being checked which will
show you a warning when they do not meet. You can use Yii framework without them but some specific
functionality may be not available in this case.
</p>
<h3>Conclusion</h3>
<?php if ($summary['errors'] > 0): ?>
<div class="alert alert-danger">
<strong>Unfortunately your server configuration does not satisfy the requirements by this application.<br>Please refer to the table below for detailed explanation.</strong>
</div>
<?php elseif ($summary['warnings'] > 0): ?>
<div class="alert alert-info">
<strong>Your server configuration satisfies the minimum requirements by this application.<br>Please pay attention to the warnings listed below and check if your application will use the corresponding features.</strong>
</div>
<?php else: ?>
<div class="alert alert-success">
<strong>Congratulations! Your server configuration satisfies all requirements.</strong>
</div>
<?php endif; ?>
<h3>Details</h3>
<table class="table table-bordered">
<tr><th>Name</th><th>Result</th><th>Required By</th><th>Memo</th></tr>
<?php foreach ($requirements as $requirement): ?>
<tr class="<?php echo $requirement['condition'] ? 'success' : ($requirement['mandatory'] ? 'danger' : 'warning') ?>">
<td>
<?php echo $requirement['name'] ?>
</td>
<td>
<span class="result"><?php echo $requirement['condition'] ? 'Passed' : ($requirement['mandatory'] ? 'Failed' : 'Warning') ?></span>
</td>
<td>
<?php echo $requirement['by'] ?>
</td>
<td>
<?php echo $requirement['memo'] ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</main>
<hr>
<footer>
<p>Server: <?php echo $this->getServerInfo() . ' ' . $this->getNowDate() ?></p>
<p>Powered by <a href="http://www.yiiframework.com/" rel="external">Yii Framework</a></p>
</footer>
</div>
</body>
</html>