Added new (clean) yii boilerplate
This commit is contained in:
parent
1d6d975a16
commit
99d29b432b
31
LICENSE
Normal file
31
LICENSE
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
The Yii framework is free software. It is released under the terms of
|
||||||
|
the following BSD License.
|
||||||
|
|
||||||
|
Copyright (c) 2008-2013 by Yii Software LLC (http://www.yiisoft.com)
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Yii Software LLC nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
64
README
Normal file
64
README
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Yii Web Programming Framework
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Thank you for choosing Yii - a high-performance component-based PHP framework.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
------------
|
||||||
|
|
||||||
|
Please make sure the release file is unpacked under a Web-accessible
|
||||||
|
directory. You shall see the following files and directories:
|
||||||
|
|
||||||
|
demos/ demos
|
||||||
|
framework/ framework source files
|
||||||
|
requirements/ requirement checker
|
||||||
|
CHANGELOG describing changes in every Yii release
|
||||||
|
LICENSE license of Yii
|
||||||
|
README this file
|
||||||
|
UPGRADE upgrading instructions
|
||||||
|
|
||||||
|
|
||||||
|
REQUIREMENTS
|
||||||
|
------------
|
||||||
|
|
||||||
|
The minimum requirement by Yii is that your Web server supports
|
||||||
|
PHP 5.1.0 or above. Yii has been tested with Apache HTTP server
|
||||||
|
on Windows and Linux operating systems.
|
||||||
|
|
||||||
|
Please access the following URL to check if your Web server reaches
|
||||||
|
the requirements by Yii, assuming "YiiPath" is where Yii is installed:
|
||||||
|
|
||||||
|
http://hostname/YiiPath/requirements/index.php
|
||||||
|
|
||||||
|
|
||||||
|
QUICK START
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Yii comes with a command line tool called "yiic" that can create
|
||||||
|
a skeleton Yii application for you to start with.
|
||||||
|
|
||||||
|
On command line, type in the following commands:
|
||||||
|
|
||||||
|
$ cd YiiPath/framework (Linux)
|
||||||
|
cd YiiPath\framework (Windows)
|
||||||
|
|
||||||
|
$ ./yiic webapp ../testdrive (Linux)
|
||||||
|
yiic webapp ..\testdrive (Windows)
|
||||||
|
|
||||||
|
The new Yii application will be created at "YiiPath/testdrive".
|
||||||
|
You can access it with the following URL:
|
||||||
|
|
||||||
|
http://hostname/YiiPath/testdrive/index.php
|
||||||
|
|
||||||
|
|
||||||
|
WHAT's NEXT
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Please visit the project website for tutorials, class reference
|
||||||
|
and join discussions with other Yii users.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The Yii Developer Team
|
||||||
|
http://www.yiiframework.com
|
497
UPGRADE
Normal file
497
UPGRADE
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
Upgrading Instructions for Yii Framework v1.1.14
|
||||||
|
================================================
|
||||||
|
|
||||||
|
!!!IMPORTANT!!!
|
||||||
|
|
||||||
|
The following upgrading instructions are cumulative. That is,
|
||||||
|
if you want to upgrade from version A to version C and there is
|
||||||
|
version B between A and C, you need to following the instructions
|
||||||
|
for both A and B.
|
||||||
|
|
||||||
|
General upgrade instructions
|
||||||
|
----------------------------
|
||||||
|
- Make a backup.
|
||||||
|
- Clean up your 'assets' folder.
|
||||||
|
- Replace 'framework' dir with the new one or point GIT to a fresh
|
||||||
|
release and update.
|
||||||
|
- Check if everything is OK, if not — revert from backup and post
|
||||||
|
issues to Yii issue tracker.
|
||||||
|
|
||||||
|
Upgrading from v1.1.13
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- CActiveRecord::count() now respects group by and having. If your code relied
|
||||||
|
on ignoring it your application may break and should be updated.
|
||||||
|
|
||||||
|
- Vendors: phlymail's Net_IDNA was replaced by PEAR Net_IDNA2. The latter library is better maintained than the former.
|
||||||
|
In case your code relies on bundled phlymail's Net_IDNA you should change it a bit. Old way of encoding IDNs:
|
||||||
|
|
||||||
|
require_once(Yii::getPathOfAlias('system.vendors.idna_convert').DIRECTORY_SEPARATOR.'idna_convert.class.php');
|
||||||
|
$idnaConvert=new idna_convert();
|
||||||
|
$result=$idnaConvert->encode($value);
|
||||||
|
|
||||||
|
New:
|
||||||
|
|
||||||
|
require_once(Yii::getPathOfAlias('system.vendors.Net_IDNA2.Net').DIRECTORY_SEPARATOR.'IDNA2.php');
|
||||||
|
$idna=new Net_IDNA2();
|
||||||
|
$result=$idna->encode($value);
|
||||||
|
|
||||||
|
- CAPTCHA appearance has been changed. Non-free Duality.ttf font (used by CCaptchaAction) replaced by open/free
|
||||||
|
SpicyRice.ttf. New font is distributed under SIL Open Font License version 1.1. Do not forget to adjust font path
|
||||||
|
in case your application relies on Duality.ttf font file.
|
||||||
|
|
||||||
|
- CSecurityManager::computeHMAC() method is now public and third parameter has been added. You must change signature
|
||||||
|
of this method in the extended child class to fit new circumstances. Otherwise an E_STRICT error will be issued.
|
||||||
|
|
||||||
|
- CClientScript::registerScriptFile() and CClientScript::registerScript() methods signature changed.
|
||||||
|
Update your subclasses that override registerScriptFile() or registerScript() if any.
|
||||||
|
|
||||||
|
- CActiveRecord::refreshMetaData() now clears meta data for all objects of the particular Active Record class.
|
||||||
|
Also CActiveRecord::refreshMetaData() will not create new meta data at once - new CActiveRecordMetaData instance
|
||||||
|
will be created on the first demand.
|
||||||
|
|
||||||
|
- Oracle related note: in case you're using COciSchema::resetSequence() or CDbSchema::resetSequence() methods with
|
||||||
|
the Oracle database, keep in mind that its behavior has changed to be consistent with the same methods for
|
||||||
|
the other database management systems. Please refer to its documentation for more details and don't forget
|
||||||
|
to adjust your code respectively.
|
||||||
|
|
||||||
|
- Signature of the CJuiInputWidget::resolveNameID() method has changed. If you're overriding this method you must
|
||||||
|
change your code to fit it. This method now accepts two parameters, it means you have to adjust code of the
|
||||||
|
overlapped descendant method signature as follows:
|
||||||
|
|
||||||
|
protected function resolveNameID($nameProperty='name',$attributeProperty='attribute')
|
||||||
|
|
||||||
|
And ancestor method call to:
|
||||||
|
|
||||||
|
parent::resolveNameID($nameProperty,$attributeProperty);
|
||||||
|
|
||||||
|
- In case you've used your own relation types extended from `CHasOneRelation` or
|
||||||
|
`CHasManyRelation` make sure you update these to reflect moving `through` property
|
||||||
|
from both these to `CActiveRelation`.
|
||||||
|
|
||||||
|
- CSecurityManager::generateRandomKey() has been deprecated in favor of CSecurityManager::generateRandomString().
|
||||||
|
Try not to use it anymore and avoid CSecurityManager::generateRandomKey() method in your code.
|
||||||
|
|
||||||
|
Upgrading from v1.1.12
|
||||||
|
----------------------
|
||||||
|
- Both jQuery and jQueryUI were updated. Check [jQuery UI upgrade guide](http://jqueryui.com/upgrade-guide/1.9/)
|
||||||
|
and [jQuery release notes](http://blog.jquery.com/2012/08/09/jquery-1-8-released/).
|
||||||
|
|
||||||
|
- We completed the behavior of CFormatter::sizeFormat() which has been introduced in Yii 1.1.11. If you are using it with your own translation file
|
||||||
|
you have to move your translations to the `yii` category in `yii.php` file which is handled by the application component `coreMessages`.
|
||||||
|
We also fixed the default translation strings to be correct English and apply to choice format, so you have to adjust your translation file keys.
|
||||||
|
|
||||||
|
- Be sure to clean all your existing cache during the upgrade. The cache values from CDbCommand has been
|
||||||
|
changed to an array to be able to store false values (returned when no records are found).
|
||||||
|
|
||||||
|
- Make sure all your event handlers in behaviors are public methods, as we are not supporting protected methods as event handlers anymore.
|
||||||
|
|
||||||
|
- We fixed the calls to CActiveRecord::beforeFind() for consistency so that beforeFind() now always gets called for every relation
|
||||||
|
on eager loading even if the main query does not return a result. This has been the case for all CActiveRecord::find*()-methods
|
||||||
|
already but now also applies for findBySql() and findAllBySql().
|
||||||
|
|
||||||
|
- Criteria modification in CActiveRecord::beforeFind() did not apply to the query when model was loaded in a relational context.
|
||||||
|
Since version 1.1.13 changes to query criteria made in beforeFind() now also apply to the query when model is loaded in a relational context.
|
||||||
|
The main problem here is that you can not use the `t`-alias for your table anymore, you have to change your code to
|
||||||
|
use the table alias currently in use as this is different in relational context.
|
||||||
|
You can get that alias by calling `$this->getTableAlias();` in your active record class
|
||||||
|
or `$this->owner->getTableAlias()` in behavior context.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$criteria->condition = 't.myfield = 1';
|
||||||
|
You need to change that to:
|
||||||
|
$alias = $this->owner->getTableAlias();
|
||||||
|
$criteria->condition = $alias.'.myfield = 1';
|
||||||
|
|
||||||
|
- Make sure you are using `CCaptcha::checkRequirements()` method for checking whether CAPTCHA could be rendered successfully in your environment.
|
||||||
|
`extension_loaded('gd')` expression is not enough and wrong because CAPTCHA could be rendered via ImageMagick with fallback to GD since 1.1.13
|
||||||
|
(thus checking code is not simple as it seems).
|
||||||
|
|
||||||
|
- In case you're using MSSQL driver make sure authentication credentials you use have permissions to use
|
||||||
|
`sys.extended_properties` system view. This is critical for retrieving additional metadata on tables.
|
||||||
|
|
||||||
|
Upgrading from v1.1.11
|
||||||
|
----------------------
|
||||||
|
- Changes in CCookieCollection::add() (introduced in 1.1.11) were reverted as they were triggering E_STRICT on some old PHP-versions
|
||||||
|
If your application relies on these newly added changes. You should change your code from
|
||||||
|
|
||||||
|
$cookies->add(new CHttpCookie($name, $value));
|
||||||
|
to
|
||||||
|
$cookies[$name] = new CHttpCookie($name, $value);
|
||||||
|
|
||||||
|
|
||||||
|
- CActiveRecord::resetScope() method signature changed. Please update your subclasses that override resetScope() if any.
|
||||||
|
|
||||||
|
Upgrading from v1.1.10
|
||||||
|
----------------------
|
||||||
|
- API of public method CConsoleCommand::confirm() changed. If you are overriding this method make sure to update your code.
|
||||||
|
The method now has a 2nd parameter for the default value which will be used if no selection is made, so you have to
|
||||||
|
adjust the signature to fit
|
||||||
|
|
||||||
|
public function confirm($message,$default=false)
|
||||||
|
|
||||||
|
and the parent call to
|
||||||
|
|
||||||
|
parent::confirm($message,$default);
|
||||||
|
|
||||||
|
- API of protected method CConsoleCommand::afterAction() changed, if you are overriding this method make sure to update your code.
|
||||||
|
method now has a 3rd parameter for application exit code, so you have to adjust the signature to fit
|
||||||
|
|
||||||
|
protected function afterAction($action,$params,$exitCode=0)
|
||||||
|
|
||||||
|
and the parent call to
|
||||||
|
|
||||||
|
parent::afterAction($action,$params,$exitCode);
|
||||||
|
|
||||||
|
- CDateFormat::format() now will return null if the parameter $time is null. Previously it would return 1/1/1970.
|
||||||
|
|
||||||
|
- If you are using CJavaScript::encode in your application with parameter coming
|
||||||
|
from user input, set second argument to true:
|
||||||
|
|
||||||
|
CJavaScript::encode($userInput, true);
|
||||||
|
|
||||||
|
It will disable prefixing parameters with "js:". If you need to pass JavaScript
|
||||||
|
expression it's now preferrable to wrap these with CJavaScriptExpression:
|
||||||
|
|
||||||
|
CJavaScript::encode(new CJavaScriptExpression('alert("Yii!");'), true);
|
||||||
|
|
||||||
|
Note that second "safe" parameter doesn't affect CJavaScriptExpression in any way.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.9
|
||||||
|
---------------------
|
||||||
|
- Previously xSendFile() was returning false if the file was not found.
|
||||||
|
This has been removed to allow relative file paths. If you are relying on this check,
|
||||||
|
you will need to do it manually before calling xSendFile().
|
||||||
|
|
||||||
|
Upgrading from v1.1.8
|
||||||
|
---------------------
|
||||||
|
- CConfiguration::createObject, CController::paginate and CHtml::getActiveId deprecated since 1.0.x were removed. Use
|
||||||
|
Yii::createComponent, new CPagination directly and CHtml::activeId respectively.
|
||||||
|
|
||||||
|
- In CErrorHandler::handleException() the checking for ajax call has been removed
|
||||||
|
as it was preventing to customize the display of the exception during an ajax call.
|
||||||
|
|
||||||
|
- Previously in case of validation error the CSS "error" class was not added to the row container at all when
|
||||||
|
checkBoxList or radioButtonList where used. This is fixed now and proper "error" CSS class is added to the row
|
||||||
|
container but in case of validation error, all labels from the list are shown in red because of the CSS rule.
|
||||||
|
|
||||||
|
To fix this and display only the attribute label in red:
|
||||||
|
|
||||||
|
in <projectdir>/css/form.css the line:
|
||||||
|
|
||||||
|
div.form div.error label
|
||||||
|
|
||||||
|
should be changed to
|
||||||
|
|
||||||
|
div.form div.error label:first-child
|
||||||
|
|
||||||
|
- If you've used "through" ActiveRecord option in your relation definitions it's good to update code as shown below.
|
||||||
|
Old style of defining this option still works but is now deprecated.
|
||||||
|
|
||||||
|
Change
|
||||||
|
|
||||||
|
~~~
|
||||||
|
class Group extends CActiveRecord
|
||||||
|
{
|
||||||
|
public function relations()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'roles'=>array(self::HAS_MANY,'Role','group_id'),
|
||||||
|
'users'=>array(self::HAS_MANY,'User','user_id','through'=>'roles'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
to
|
||||||
|
|
||||||
|
~~~
|
||||||
|
class Group extends CActiveRecord
|
||||||
|
{
|
||||||
|
public function relations()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'roles'=>array(self::HAS_MANY,'Role','group_id'),
|
||||||
|
'users'=>array(self::HAS_MANY,'User',array('user_id'=>'id'),'through'=>'roles'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.7
|
||||||
|
---------------------
|
||||||
|
- CDbAuthManager will now quote columns and tables referenced in its SQL code.
|
||||||
|
If your auth tables were created in a case-insensitive fashion (e.g. on PostgreSQL)
|
||||||
|
while your DBMS is case-sensitive, this change may cause DB query errors.
|
||||||
|
To fix this issue, you will have to rename the table names and columns, or re-create
|
||||||
|
the auth tables by following the SQL code given in framework/web/auth/*.sql.
|
||||||
|
- jQuery was upgraded to 1.6.1. Check your client side code and if you have issues consider
|
||||||
|
downgrading to 1.5.1 or 1.4.4.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.6
|
||||||
|
---------------------
|
||||||
|
- Make sure you are using latest stable PHPUnit 3.5 if you are using unit tests.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.5
|
||||||
|
---------------------
|
||||||
|
- In CActiveRecord::relations(), if a relation involves composite foreign keys, the foreign key
|
||||||
|
columns must be separated by commas now. Previously, the columns can be separated by either
|
||||||
|
commas or spaces. If your mode code are generated by Gii or yiic shell, you do not need to
|
||||||
|
worry about this.
|
||||||
|
- CLDR data was updated to a newest available version so data formats, month
|
||||||
|
names and other regional data can be changed.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.4
|
||||||
|
---------------------
|
||||||
|
- CHtml will no longer render null attributes for HTML tags. This means if $htmlOptions is
|
||||||
|
array('class'=>null), it will no longer render the 'class' attribute in the HTML tag.
|
||||||
|
it would render the class attribute as class="". We expect this will not cause much trouble
|
||||||
|
in upgrading. However, in case problems happen, you may set the attribute to be an empty string
|
||||||
|
to solve them.
|
||||||
|
|
||||||
|
- Now by default CWebLogRoute does not render logs in FireBug for ajax calls.
|
||||||
|
To get logs rendered for ajax calls in FireBug set CWebLogRoute::ignoreAjaxInFireBug to false
|
||||||
|
|
||||||
|
- The implementation of CCache::flush() was changed a little. Child classes should now implement
|
||||||
|
a flushValues() method. If you use any custom cache class with flush functionality, you should
|
||||||
|
rename the flush method accordingly.
|
||||||
|
|
||||||
|
- The prompt and empty options used in CHtml methods will NOT be HTML-encoded anymore. It will now
|
||||||
|
always convert ">" and "<" into ">" and "lt;", respectively. This should be sufficient in most
|
||||||
|
cases. But if your application uses some other special characters, or if you allow user inputs
|
||||||
|
to be used as prompt and empty text labels, please call CHtml::encode() explicitly on these option
|
||||||
|
values.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.3
|
||||||
|
---------------------
|
||||||
|
- Zii was merged into Yii so if you are using SVN to keep framework
|
||||||
|
up to date, you'll need to delete 'framework/zii' and then update it.
|
||||||
|
|
||||||
|
Upgrading from v1.1.2
|
||||||
|
---------------------
|
||||||
|
- When using the skin feature, you now need to explicitly configure
|
||||||
|
the 'enableSkin' property of 'widgetFactory' application component
|
||||||
|
to be true. Also, if you have configured the 'widgets' property, you
|
||||||
|
should rename it to be 'skinnableWidgets'. These changes are due to
|
||||||
|
the introduction of the global widget customization feature.
|
||||||
|
Please see the guide (the "Theming" section) for more details.
|
||||||
|
|
||||||
|
- CAutoComplete is now deprecated and will be removed in Yii 1.2. Consider
|
||||||
|
using CJuiAutoComplete.
|
||||||
|
|
||||||
|
- Now it's not possible to reuse CActiveFinder. So if you have code like this:
|
||||||
|
|
||||||
|
$finder = Post::model()->with('comments');
|
||||||
|
$posts1 = $finder->findAll();
|
||||||
|
…
|
||||||
|
$posts2 = $finder->findAll();
|
||||||
|
|
||||||
|
you should rewrite it to:
|
||||||
|
|
||||||
|
$posts1 = Post::model()->with('comments');
|
||||||
|
…
|
||||||
|
$posts2 = Post::model()->with('comments');
|
||||||
|
|
||||||
|
- The 'condition' declared in the scopes of the related AR classes will now
|
||||||
|
be put in the 'ON' clause of the JOIN statement when performing relational AR queries.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1.1
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Upgrading from v1.1.0
|
||||||
|
---------------------
|
||||||
|
- CHtml::beginForm() will automatically generate hidden fields to represent
|
||||||
|
the parameters in the query string when the form uses GET method.
|
||||||
|
To avoid submitting duplicated query parameters, you may use createUrl()
|
||||||
|
to explicitly specify the action of the form. You may also need to remove
|
||||||
|
the hidden fields that you previously render for the same purpose.
|
||||||
|
|
||||||
|
- The code generated by yiic tool is changed. If you are using yiic tool
|
||||||
|
to generate new CRUD code on a previously generated skeleton, you are recommended
|
||||||
|
to run "yiic webapp" again to re-generate the skeleton. Make sure you backup
|
||||||
|
your work before you do this.
|
||||||
|
|
||||||
|
- CMenu now renders the 'active' CSS class for the 'li' tag instead of the
|
||||||
|
hyperlink tag. You should adjust your CSS code accordingly if you use CMenu
|
||||||
|
in your application.
|
||||||
|
|
||||||
|
- CUrlManager::parsePathInfo() is changed to be non-static. If you override
|
||||||
|
this method or your existing code calls this method directly (neither is common),
|
||||||
|
you need to change your code accordingly.
|
||||||
|
|
||||||
|
- CController::forward() will exit the application by default now. If you want
|
||||||
|
to keep the old behavior, you may pass false as the second parameter.
|
||||||
|
|
||||||
|
- The jQuery copy included in the framework has been upgraded to version 1.4.2.
|
||||||
|
This may cause some incompatibility problems to your existing jQuery code or plugins.
|
||||||
|
If you want to keep using version 1.3.2, you may do so by configuring
|
||||||
|
CClientScript::scriptMap property.
|
||||||
|
|
||||||
|
- The default theme for JQuery UI widgets was changed from 'smoothness' to 'base'.
|
||||||
|
If you are using 'smoothness', you will need to manually download this theme from
|
||||||
|
jqueryui.com and configure the 'theme' property of the JQuery UI widgets accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.1rc
|
||||||
|
---------------------
|
||||||
|
- CRudColumn is renamed as CButtonColumn
|
||||||
|
- CDataColumn.dataField and dataExpression are renamed as name and value, respectively
|
||||||
|
- The alias name for the primary table in an AR query is fixed to be 't'
|
||||||
|
|
||||||
|
Upgrading from v1.1b
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Upgrading from v1.1a
|
||||||
|
--------------------
|
||||||
|
- CSort::attributes is changed. Now the array keys refer to attribute names
|
||||||
|
and array values refer to virtual attribute definitions. Please read the
|
||||||
|
API documentation for this property to learn more details. This change will
|
||||||
|
affect your code only when you explicitly specify this property.
|
||||||
|
|
||||||
|
Upgrading from v1.0.x
|
||||||
|
---------------------
|
||||||
|
- Application and module parameter names are changed to be case-sensitive.
|
||||||
|
In 1.0.x, they are case-insensitive.
|
||||||
|
|
||||||
|
- For tabular input, using Field[$i] is not valid anymore. Attribute names
|
||||||
|
should look like [$i]Field in order to support array-typed fields
|
||||||
|
(e.g. [$i]Field[$index]).
|
||||||
|
|
||||||
|
- Please read the Guide for further details on how to upgrade from v1.0.x to v1.1.
|
||||||
|
|
||||||
|
Upgrading from v1.0.12
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Upgrading from v1.0.11
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Upgrading from v1.0.10
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Upgrading from v1.0.9
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Upgrading from v1.0.8
|
||||||
|
---------------------
|
||||||
|
- ActiveRecord lazy loading is changed for optimization purpose. Previously,
|
||||||
|
when lazy loading occurs, the related table will be joined with the primary
|
||||||
|
table. Now, the related table will be queried without joining the primary table.
|
||||||
|
As a result, if you are using lazy loading and the corresponding relation
|
||||||
|
declaration includes reference to the primary table, the query will fail.
|
||||||
|
To fix this problem, please specify the lazy loading query options with the
|
||||||
|
actual primary table column values.
|
||||||
|
|
||||||
|
Upgrading from v1.0.7
|
||||||
|
---------------------
|
||||||
|
- A directory imported using Yii::import() will have precedence over
|
||||||
|
any existing include paths. For example, if we import 'application.models.*',
|
||||||
|
then the corresponding directory will be searched before any other
|
||||||
|
existing include paths. This also means, a directory imported later will
|
||||||
|
have precedence over directories imported earlier. Previously, this order
|
||||||
|
was reversed. This change may affect you if you have several classes with
|
||||||
|
the same name and they are imported via different directories. You will need
|
||||||
|
to adjust the import order of these directories to make sure your existing
|
||||||
|
applications are not broken due to this change.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.6
|
||||||
|
---------------------
|
||||||
|
- Default named scope will no longer be applied to INSERT, UPDATE and
|
||||||
|
DELETE queries. It is only applied to SELECT queries. You should be aware
|
||||||
|
of this change if you override CActiveRecord::defaultScope() in your code.
|
||||||
|
|
||||||
|
- The signature of CWebUser::logout() is changed. If you override this method,
|
||||||
|
you will need to modify your method declaration accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.5
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.4
|
||||||
|
---------------------
|
||||||
|
- CWebUser::checkAccess() takes an additional parameter to allow caching
|
||||||
|
the access check results. If you override this method, you will need to
|
||||||
|
modify your method declaration accordingly. Because the new parameter will
|
||||||
|
enable caching the access check results by default, please double check
|
||||||
|
your code containing this method call to make sure the behavior is as expected.
|
||||||
|
|
||||||
|
- CDateParser has been renamed to CDateTimeParser
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.3
|
||||||
|
---------------------
|
||||||
|
- The signature of CWebModule::init() is modified. Its parameter is removed.
|
||||||
|
If your application uses modules, you have to modify your module class
|
||||||
|
files accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.2
|
||||||
|
---------------------
|
||||||
|
- Controllers that are organized in subdirectories are now referenced
|
||||||
|
using the ID format "path/to/xyz". Previously it was "path.to.xyz".
|
||||||
|
If you use "path.to.xyz" in your application, you have to modify it
|
||||||
|
to "path/to/xyz".
|
||||||
|
|
||||||
|
- CHtml::coreScript() is removed. If you used this in your application,
|
||||||
|
please use the following alternative:
|
||||||
|
|
||||||
|
Yii::app()->clientScript->registerCoreScript($name);
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.1
|
||||||
|
---------------------
|
||||||
|
- Due to the introduction of the scenario-based massive assignment feature,
|
||||||
|
we removed CActiveRecord::protectedAttributes(). Please use safeAttributes()
|
||||||
|
to specify which attributes are safe to be massively assigned.
|
||||||
|
|
||||||
|
For more details about scenario-based assignment and validation,
|
||||||
|
please read the following tutorial section:
|
||||||
|
|
||||||
|
http://www.yiiframework.com/doc/guide/form.model#securing-attribute-assignments
|
||||||
|
|
||||||
|
- The signature of CModel::validate() has been changed to:
|
||||||
|
|
||||||
|
CModel::validate($scenario='', $attributes=null)
|
||||||
|
|
||||||
|
That is, the order of the two parameters has been swapped. If your application
|
||||||
|
contains code that invokes the validate() method (of either a CFormModel
|
||||||
|
or a CActiveRecord object) with some parameter, please make sure you fix
|
||||||
|
the parameter order.
|
||||||
|
|
||||||
|
- The usage of CActiveRecord::with() and the 'with' option in relations
|
||||||
|
has been changed. In order to query child relations, we should specify the 'with'
|
||||||
|
parameter/option like the following now:
|
||||||
|
|
||||||
|
Post::model()->with(array('comments', 'author.profile'))->findAll();
|
||||||
|
|
||||||
|
Previously, this should be written as:
|
||||||
|
|
||||||
|
Post::model()->with(array('comments', 'author'=>'profile'))->findAll();
|
||||||
|
|
||||||
|
If your code does not involve child relations (like 'profile' in the above),
|
||||||
|
nothing needs to be changed.
|
||||||
|
|
||||||
|
This change has been introduced in order to support dynamic relational
|
||||||
|
query options. For example, we can specify that comments be sorted in
|
||||||
|
descending order (assuming in the relations() method it is specified as
|
||||||
|
ascending order):
|
||||||
|
|
||||||
|
Post::model()->with(array(
|
||||||
|
'comments'=>array('order'=>'createTime DESC'),
|
||||||
|
'author.profile',
|
||||||
|
))->findAll();
|
||||||
|
|
||||||
|
|
||||||
|
Upgrading from v1.0.0
|
||||||
|
---------------------
|
||||||
|
- An $scenario parameter is added to both CModel::beforeValidate() and afterValidate().
|
||||||
|
If you override these methods in your child classes (form models, AR classes),
|
||||||
|
make sure you change the method signature accordingly.
|
18
demos/phonebook/flex/.project
Normal file
18
demos/phonebook/flex/.project
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>phonebook</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.adobe.flexbuilder.project.flexbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.adobe.flexbuilder.project.flexnature</nature>
|
||||||
|
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
@ -0,0 +1,3 @@
|
|||||||
|
#Wed Oct 08 08:18:30 EDT 2008
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
upgradeSDK/fb3=
|
@ -0,0 +1,3 @@
|
|||||||
|
#Wed Oct 08 03:19:04 GMT 2008
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=utf-8
|
1
framework/.htaccess
Normal file
1
framework/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
deny from all
|
870
framework/YiiBase.php
Normal file
870
framework/YiiBase.php
Normal file
@ -0,0 +1,870 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* YiiBase class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
* @package system
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the application start timestamp.
|
||||||
|
*/
|
||||||
|
defined('YII_BEGIN_TIME') or define('YII_BEGIN_TIME',microtime(true));
|
||||||
|
/**
|
||||||
|
* This constant defines whether the application should be in debug mode or not. Defaults to false.
|
||||||
|
*/
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG',false);
|
||||||
|
/**
|
||||||
|
* This constant defines how much call stack information (file name and line number) should be logged by Yii::trace().
|
||||||
|
* Defaults to 0, meaning no backtrace information. If it is greater than 0,
|
||||||
|
* at most that number of call stacks will be logged. Note, only user application call stacks are considered.
|
||||||
|
*/
|
||||||
|
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',0);
|
||||||
|
/**
|
||||||
|
* This constant defines whether exception handling should be enabled. Defaults to true.
|
||||||
|
*/
|
||||||
|
defined('YII_ENABLE_EXCEPTION_HANDLER') or define('YII_ENABLE_EXCEPTION_HANDLER',true);
|
||||||
|
/**
|
||||||
|
* This constant defines whether error handling should be enabled. Defaults to true.
|
||||||
|
*/
|
||||||
|
defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER',true);
|
||||||
|
/**
|
||||||
|
* Defines the Yii framework installation path.
|
||||||
|
*/
|
||||||
|
defined('YII_PATH') or define('YII_PATH',dirname(__FILE__));
|
||||||
|
/**
|
||||||
|
* Defines the Zii library installation path.
|
||||||
|
*/
|
||||||
|
defined('YII_ZII_PATH') or define('YII_ZII_PATH',YII_PATH.DIRECTORY_SEPARATOR.'zii');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* YiiBase is a helper class serving common framework functionalities.
|
||||||
|
*
|
||||||
|
* Do not use YiiBase directly. Instead, use its child class {@link Yii} where
|
||||||
|
* you can customize methods of YiiBase.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class YiiBase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array class map used by the Yii autoloading mechanism.
|
||||||
|
* The array keys are the class names and the array values are the corresponding class file paths.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
public static $classMap=array();
|
||||||
|
/**
|
||||||
|
* @var boolean whether to rely on PHP include path to autoload class files. Defaults to true.
|
||||||
|
* You may set this to be false if your hosting environment doesn't allow changing the PHP
|
||||||
|
* include path, or if you want to append additional autoloaders to the default Yii autoloader.
|
||||||
|
* @since 1.1.8
|
||||||
|
*/
|
||||||
|
public static $enableIncludePath=true;
|
||||||
|
|
||||||
|
private static $_aliases=array('system'=>YII_PATH,'zii'=>YII_ZII_PATH); // alias => path
|
||||||
|
private static $_imports=array(); // alias => class name or directory
|
||||||
|
private static $_includePaths; // list of include paths
|
||||||
|
private static $_app;
|
||||||
|
private static $_logger;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the version of Yii framework
|
||||||
|
*/
|
||||||
|
public static function getVersion()
|
||||||
|
{
|
||||||
|
return '1.1.14';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Web application instance.
|
||||||
|
* @param mixed $config application configuration.
|
||||||
|
* If a string, it is treated as the path of the file that contains the configuration;
|
||||||
|
* If an array, it is the actual configuration information.
|
||||||
|
* Please make sure you specify the {@link CApplication::basePath basePath} property in the configuration,
|
||||||
|
* which should point to the directory containing all application logic, template and data.
|
||||||
|
* If not, the directory will be defaulted to 'protected'.
|
||||||
|
* @return CWebApplication
|
||||||
|
*/
|
||||||
|
public static function createWebApplication($config=null)
|
||||||
|
{
|
||||||
|
return self::createApplication('CWebApplication',$config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a console application instance.
|
||||||
|
* @param mixed $config application configuration.
|
||||||
|
* If a string, it is treated as the path of the file that contains the configuration;
|
||||||
|
* If an array, it is the actual configuration information.
|
||||||
|
* Please make sure you specify the {@link CApplication::basePath basePath} property in the configuration,
|
||||||
|
* which should point to the directory containing all application logic, template and data.
|
||||||
|
* If not, the directory will be defaulted to 'protected'.
|
||||||
|
* @return CConsoleApplication
|
||||||
|
*/
|
||||||
|
public static function createConsoleApplication($config=null)
|
||||||
|
{
|
||||||
|
return self::createApplication('CConsoleApplication',$config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an application of the specified class.
|
||||||
|
* @param string $class the application class name
|
||||||
|
* @param mixed $config application configuration. This parameter will be passed as the parameter
|
||||||
|
* to the constructor of the application class.
|
||||||
|
* @return mixed the application instance
|
||||||
|
*/
|
||||||
|
public static function createApplication($class,$config=null)
|
||||||
|
{
|
||||||
|
return new $class($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application singleton or null if the singleton has not been created yet.
|
||||||
|
* @return CApplication the application singleton, null if the singleton has not been created yet.
|
||||||
|
*/
|
||||||
|
public static function app()
|
||||||
|
{
|
||||||
|
return self::$_app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the application instance in the class static member.
|
||||||
|
* This method helps implement a singleton pattern for CApplication.
|
||||||
|
* Repeated invocation of this method or the CApplication constructor
|
||||||
|
* will cause the throw of an exception.
|
||||||
|
* To retrieve the application instance, use {@link app()}.
|
||||||
|
* @param CApplication $app the application instance. If this is null, the existing
|
||||||
|
* application singleton will be removed.
|
||||||
|
* @throws CException if multiple application instances are registered.
|
||||||
|
*/
|
||||||
|
public static function setApplication($app)
|
||||||
|
{
|
||||||
|
if(self::$_app===null || $app===null)
|
||||||
|
self::$_app=$app;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Yii application can only be created once.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the path of the framework
|
||||||
|
*/
|
||||||
|
public static function getFrameworkPath()
|
||||||
|
{
|
||||||
|
return YII_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an object and initializes it based on the given configuration.
|
||||||
|
*
|
||||||
|
* The specified configuration can be either a string or an array.
|
||||||
|
* If the former, the string is treated as the object type which can
|
||||||
|
* be either the class name or {@link YiiBase::getPathOfAlias class path alias}.
|
||||||
|
* If the latter, the 'class' element is treated as the object type,
|
||||||
|
* and the rest of the name-value pairs in the array are used to initialize
|
||||||
|
* the corresponding object properties.
|
||||||
|
*
|
||||||
|
* Any additional parameters passed to this method will be
|
||||||
|
* passed to the constructor of the object being created.
|
||||||
|
*
|
||||||
|
* @param mixed $config the configuration. It can be either a string or an array.
|
||||||
|
* @return mixed the created object
|
||||||
|
* @throws CException if the configuration does not have a 'class' element.
|
||||||
|
*/
|
||||||
|
public static function createComponent($config)
|
||||||
|
{
|
||||||
|
if(is_string($config))
|
||||||
|
{
|
||||||
|
$type=$config;
|
||||||
|
$config=array();
|
||||||
|
}
|
||||||
|
elseif(isset($config['class']))
|
||||||
|
{
|
||||||
|
$type=$config['class'];
|
||||||
|
unset($config['class']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Object configuration must be an array containing a "class" element.'));
|
||||||
|
|
||||||
|
if(!class_exists($type,false))
|
||||||
|
$type=Yii::import($type,true);
|
||||||
|
|
||||||
|
if(($n=func_num_args())>1)
|
||||||
|
{
|
||||||
|
$args=func_get_args();
|
||||||
|
if($n===2)
|
||||||
|
$object=new $type($args[1]);
|
||||||
|
elseif($n===3)
|
||||||
|
$object=new $type($args[1],$args[2]);
|
||||||
|
elseif($n===4)
|
||||||
|
$object=new $type($args[1],$args[2],$args[3]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unset($args[0]);
|
||||||
|
$class=new ReflectionClass($type);
|
||||||
|
// Note: ReflectionClass::newInstanceArgs() is available for PHP 5.1.3+
|
||||||
|
// $object=$class->newInstanceArgs($args);
|
||||||
|
$object=call_user_func_array(array($class,'newInstance'),$args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$object=new $type;
|
||||||
|
|
||||||
|
foreach($config as $key=>$value)
|
||||||
|
$object->$key=$value;
|
||||||
|
|
||||||
|
return $object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports a class or a directory.
|
||||||
|
*
|
||||||
|
* Importing a class is like including the corresponding class file.
|
||||||
|
* The main difference is that importing a class is much lighter because it only
|
||||||
|
* includes the class file when the class is referenced the first time.
|
||||||
|
*
|
||||||
|
* Importing a directory is equivalent to adding a directory into the PHP include path.
|
||||||
|
* If multiple directories are imported, the directories imported later will take
|
||||||
|
* precedence in class file searching (i.e., they are added to the front of the PHP include path).
|
||||||
|
*
|
||||||
|
* Path aliases are used to import a class or directory. For example,
|
||||||
|
* <ul>
|
||||||
|
* <li><code>application.components.GoogleMap</code>: import the <code>GoogleMap</code> class.</li>
|
||||||
|
* <li><code>application.components.*</code>: import the <code>components</code> directory.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* The same path alias can be imported multiple times, but only the first time is effective.
|
||||||
|
* Importing a directory does not import any of its subdirectories.
|
||||||
|
*
|
||||||
|
* Starting from version 1.1.5, this method can also be used to import a class in namespace format
|
||||||
|
* (available for PHP 5.3 or above only). It is similar to importing a class in path alias format,
|
||||||
|
* except that the dot separator is replaced by the backslash separator. For example, importing
|
||||||
|
* <code>application\components\GoogleMap</code> is similar to importing <code>application.components.GoogleMap</code>.
|
||||||
|
* The difference is that the former class is using qualified name, while the latter unqualified.
|
||||||
|
*
|
||||||
|
* Note, importing a class in namespace format requires that the namespace corresponds to
|
||||||
|
* a valid path alias once backslash characters are replaced with dot characters.
|
||||||
|
* For example, the namespace <code>application\components</code> must correspond to a valid
|
||||||
|
* path alias <code>application.components</code>.
|
||||||
|
*
|
||||||
|
* @param string $alias path alias to be imported
|
||||||
|
* @param boolean $forceInclude whether to include the class file immediately. If false, the class file
|
||||||
|
* will be included only when the class is being used. This parameter is used only when
|
||||||
|
* the path alias refers to a class.
|
||||||
|
* @return string the class name or the directory that this alias refers to
|
||||||
|
* @throws CException if the alias is invalid
|
||||||
|
*/
|
||||||
|
public static function import($alias,$forceInclude=false)
|
||||||
|
{
|
||||||
|
if(isset(self::$_imports[$alias])) // previously imported
|
||||||
|
return self::$_imports[$alias];
|
||||||
|
|
||||||
|
if(class_exists($alias,false) || interface_exists($alias,false))
|
||||||
|
return self::$_imports[$alias]=$alias;
|
||||||
|
|
||||||
|
if(($pos=strrpos($alias,'\\'))!==false) // a class name in PHP 5.3 namespace format
|
||||||
|
{
|
||||||
|
$namespace=str_replace('\\','.',ltrim(substr($alias,0,$pos),'\\'));
|
||||||
|
if(($path=self::getPathOfAlias($namespace))!==false)
|
||||||
|
{
|
||||||
|
$classFile=$path.DIRECTORY_SEPARATOR.substr($alias,$pos+1).'.php';
|
||||||
|
if($forceInclude)
|
||||||
|
{
|
||||||
|
if(is_file($classFile))
|
||||||
|
require($classFile);
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.',array('{alias}'=>$alias)));
|
||||||
|
self::$_imports[$alias]=$alias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self::$classMap[$alias]=$classFile;
|
||||||
|
return $alias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// try to autoload the class with an autoloader
|
||||||
|
if (class_exists($alias,true))
|
||||||
|
return self::$_imports[$alias]=$alias;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',
|
||||||
|
array('{alias}'=>$namespace)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($pos=strrpos($alias,'.'))===false) // a simple class name
|
||||||
|
{
|
||||||
|
if($forceInclude && self::autoload($alias))
|
||||||
|
self::$_imports[$alias]=$alias;
|
||||||
|
return $alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
$className=(string)substr($alias,$pos+1);
|
||||||
|
$isClass=$className!=='*';
|
||||||
|
|
||||||
|
if($isClass && (class_exists($className,false) || interface_exists($className,false)))
|
||||||
|
return self::$_imports[$alias]=$className;
|
||||||
|
|
||||||
|
if(($path=self::getPathOfAlias($alias))!==false)
|
||||||
|
{
|
||||||
|
if($isClass)
|
||||||
|
{
|
||||||
|
if($forceInclude)
|
||||||
|
{
|
||||||
|
if(is_file($path.'.php'))
|
||||||
|
require($path.'.php');
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.',array('{alias}'=>$alias)));
|
||||||
|
self::$_imports[$alias]=$className;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self::$classMap[$className]=$path.'.php';
|
||||||
|
return $className;
|
||||||
|
}
|
||||||
|
else // a directory
|
||||||
|
{
|
||||||
|
if(self::$_includePaths===null)
|
||||||
|
{
|
||||||
|
self::$_includePaths=array_unique(explode(PATH_SEPARATOR,get_include_path()));
|
||||||
|
if(($pos=array_search('.',self::$_includePaths,true))!==false)
|
||||||
|
unset(self::$_includePaths[$pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_unshift(self::$_includePaths,$path);
|
||||||
|
|
||||||
|
if(self::$enableIncludePath && set_include_path('.'.PATH_SEPARATOR.implode(PATH_SEPARATOR,self::$_includePaths))===false)
|
||||||
|
self::$enableIncludePath=false;
|
||||||
|
|
||||||
|
return self::$_imports[$alias]=$path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Alias "{alias}" is invalid. Make sure it points to an existing directory or file.',
|
||||||
|
array('{alias}'=>$alias)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates an alias into a file path.
|
||||||
|
* Note, this method does not ensure the existence of the resulting file path.
|
||||||
|
* It only checks if the root alias is valid or not.
|
||||||
|
* @param string $alias alias (e.g. system.web.CController)
|
||||||
|
* @return mixed file path corresponding to the alias, false if the alias is invalid.
|
||||||
|
*/
|
||||||
|
public static function getPathOfAlias($alias)
|
||||||
|
{
|
||||||
|
if(isset(self::$_aliases[$alias]))
|
||||||
|
return self::$_aliases[$alias];
|
||||||
|
elseif(($pos=strpos($alias,'.'))!==false)
|
||||||
|
{
|
||||||
|
$rootAlias=substr($alias,0,$pos);
|
||||||
|
if(isset(self::$_aliases[$rootAlias]))
|
||||||
|
return self::$_aliases[$alias]=rtrim(self::$_aliases[$rootAlias].DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,substr($alias,$pos+1)),'*'.DIRECTORY_SEPARATOR);
|
||||||
|
elseif(self::$_app instanceof CWebApplication)
|
||||||
|
{
|
||||||
|
if(self::$_app->findModule($rootAlias)!==null)
|
||||||
|
return self::getPathOfAlias($alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a path alias.
|
||||||
|
* Note, this method neither checks the existence of the path nor normalizes the path.
|
||||||
|
* @param string $alias alias to the path
|
||||||
|
* @param string $path the path corresponding to the alias. If this is null, the corresponding
|
||||||
|
* path alias will be removed.
|
||||||
|
*/
|
||||||
|
public static function setPathOfAlias($alias,$path)
|
||||||
|
{
|
||||||
|
if(empty($path))
|
||||||
|
unset(self::$_aliases[$alias]);
|
||||||
|
else
|
||||||
|
self::$_aliases[$alias]=rtrim($path,'\\/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class autoload loader.
|
||||||
|
* This method is provided to be invoked within an __autoload() magic method.
|
||||||
|
* @param string $className class name
|
||||||
|
* @return boolean whether the class has been loaded successfully
|
||||||
|
*/
|
||||||
|
public static function autoload($className)
|
||||||
|
{
|
||||||
|
// use include so that the error PHP file may appear
|
||||||
|
if(isset(self::$classMap[$className]))
|
||||||
|
include(self::$classMap[$className]);
|
||||||
|
elseif(isset(self::$_coreClasses[$className]))
|
||||||
|
include(YII_PATH.self::$_coreClasses[$className]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// include class file relying on include_path
|
||||||
|
if(strpos($className,'\\')===false) // class without namespace
|
||||||
|
{
|
||||||
|
if(self::$enableIncludePath===false)
|
||||||
|
{
|
||||||
|
foreach(self::$_includePaths as $path)
|
||||||
|
{
|
||||||
|
$classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
|
||||||
|
if(is_file($classFile))
|
||||||
|
{
|
||||||
|
include($classFile);
|
||||||
|
if(YII_DEBUG && basename(realpath($classFile))!==$className.'.php')
|
||||||
|
throw new CException(Yii::t('yii','Class name "{class}" does not match class file "{file}".', array(
|
||||||
|
'{class}'=>$className,
|
||||||
|
'{file}'=>$classFile,
|
||||||
|
)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
include($className.'.php');
|
||||||
|
}
|
||||||
|
else // class name with namespace in PHP 5.3
|
||||||
|
{
|
||||||
|
$namespace=str_replace('\\','.',ltrim($className,'\\'));
|
||||||
|
if(($path=self::getPathOfAlias($namespace))!==false)
|
||||||
|
include($path.'.php');
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return class_exists($className,false) || interface_exists($className,false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a trace message.
|
||||||
|
* This method will only log a message when the application is in debug mode.
|
||||||
|
* @param string $msg message to be logged
|
||||||
|
* @param string $category category of the message
|
||||||
|
* @see log
|
||||||
|
*/
|
||||||
|
public static function trace($msg,$category='application')
|
||||||
|
{
|
||||||
|
if(YII_DEBUG)
|
||||||
|
self::log($msg,CLogger::LEVEL_TRACE,$category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a message.
|
||||||
|
* Messages logged by this method may be retrieved via {@link CLogger::getLogs}
|
||||||
|
* and may be recorded in different media, such as file, email, database, using
|
||||||
|
* {@link CLogRouter}.
|
||||||
|
* @param string $msg message to be logged
|
||||||
|
* @param string $level level of the message (e.g. 'trace', 'warning', 'error'). It is case-insensitive.
|
||||||
|
* @param string $category category of the message (e.g. 'system.web'). It is case-insensitive.
|
||||||
|
*/
|
||||||
|
public static function log($msg,$level=CLogger::LEVEL_INFO,$category='application')
|
||||||
|
{
|
||||||
|
if(self::$_logger===null)
|
||||||
|
self::$_logger=new CLogger;
|
||||||
|
if(YII_DEBUG && YII_TRACE_LEVEL>0 && $level!==CLogger::LEVEL_PROFILE)
|
||||||
|
{
|
||||||
|
$traces=debug_backtrace();
|
||||||
|
$count=0;
|
||||||
|
foreach($traces as $trace)
|
||||||
|
{
|
||||||
|
if(isset($trace['file'],$trace['line']) && strpos($trace['file'],YII_PATH)!==0)
|
||||||
|
{
|
||||||
|
$msg.="\nin ".$trace['file'].' ('.$trace['line'].')';
|
||||||
|
if(++$count>=YII_TRACE_LEVEL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::$_logger->log($msg,$level,$category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the beginning of a code block for profiling.
|
||||||
|
* This has to be matched with a call to {@link endProfile()} with the same token.
|
||||||
|
* The begin- and end- calls must also be properly nested, e.g.,
|
||||||
|
* <pre>
|
||||||
|
* Yii::beginProfile('block1');
|
||||||
|
* Yii::beginProfile('block2');
|
||||||
|
* Yii::endProfile('block2');
|
||||||
|
* Yii::endProfile('block1');
|
||||||
|
* </pre>
|
||||||
|
* The following sequence is not valid:
|
||||||
|
* <pre>
|
||||||
|
* Yii::beginProfile('block1');
|
||||||
|
* Yii::beginProfile('block2');
|
||||||
|
* Yii::endProfile('block1');
|
||||||
|
* Yii::endProfile('block2');
|
||||||
|
* </pre>
|
||||||
|
* @param string $token token for the code block
|
||||||
|
* @param string $category the category of this log message
|
||||||
|
* @see endProfile
|
||||||
|
*/
|
||||||
|
public static function beginProfile($token,$category='application')
|
||||||
|
{
|
||||||
|
self::log('begin:'.$token,CLogger::LEVEL_PROFILE,$category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the end of a code block for profiling.
|
||||||
|
* This has to be matched with a previous call to {@link beginProfile()} with the same token.
|
||||||
|
* @param string $token token for the code block
|
||||||
|
* @param string $category the category of this log message
|
||||||
|
* @see beginProfile
|
||||||
|
*/
|
||||||
|
public static function endProfile($token,$category='application')
|
||||||
|
{
|
||||||
|
self::log('end:'.$token,CLogger::LEVEL_PROFILE,$category);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CLogger message logger
|
||||||
|
*/
|
||||||
|
public static function getLogger()
|
||||||
|
{
|
||||||
|
if(self::$_logger!==null)
|
||||||
|
return self::$_logger;
|
||||||
|
else
|
||||||
|
return self::$_logger=new CLogger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the logger object.
|
||||||
|
* @param CLogger $logger the logger object.
|
||||||
|
* @since 1.1.8
|
||||||
|
*/
|
||||||
|
public static function setLogger($logger)
|
||||||
|
{
|
||||||
|
self::$_logger=$logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string that can be displayed on your Web page showing Powered-by-Yii information
|
||||||
|
* @return string a string that can be displayed on your Web page showing Powered-by-Yii information
|
||||||
|
*/
|
||||||
|
public static function powered()
|
||||||
|
{
|
||||||
|
return Yii::t('yii','Powered by {yii}.', array('{yii}'=>'<a href="http://www.yiiframework.com/" rel="external">Yii Framework</a>'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a message to the specified language.
|
||||||
|
* This method supports choice format (see {@link CChoiceFormat}),
|
||||||
|
* i.e., the message returned will be chosen from a few candidates according to the given
|
||||||
|
* number value. This feature is mainly used to solve plural format issue in case
|
||||||
|
* a message has different plural forms in some languages.
|
||||||
|
* @param string $category message category. Please use only word letters. Note, category 'yii' is
|
||||||
|
* reserved for Yii framework core code use. See {@link CPhpMessageSource} for
|
||||||
|
* more interpretation about message category.
|
||||||
|
* @param string $message the original message
|
||||||
|
* @param array $params parameters to be applied to the message using <code>strtr</code>.
|
||||||
|
* The first parameter can be a number without key.
|
||||||
|
* And in this case, the method will call {@link CChoiceFormat::format} to choose
|
||||||
|
* an appropriate message translation.
|
||||||
|
* Starting from version 1.1.6 you can pass parameter for {@link CChoiceFormat::format}
|
||||||
|
* or plural forms format without wrapping it with array.
|
||||||
|
* This parameter is then available as <code>{n}</code> in the message translation string.
|
||||||
|
* @param string $source which message source application component to use.
|
||||||
|
* Defaults to null, meaning using 'coreMessages' for messages belonging to
|
||||||
|
* the 'yii' category and using 'messages' for the rest messages.
|
||||||
|
* @param string $language the target language. If null (default), the {@link CApplication::getLanguage application language} will be used.
|
||||||
|
* @return string the translated message
|
||||||
|
* @see CMessageSource
|
||||||
|
*/
|
||||||
|
public static function t($category,$message,$params=array(),$source=null,$language=null)
|
||||||
|
{
|
||||||
|
if(self::$_app!==null)
|
||||||
|
{
|
||||||
|
if($source===null)
|
||||||
|
$source=($category==='yii'||$category==='zii')?'coreMessages':'messages';
|
||||||
|
if(($source=self::$_app->getComponent($source))!==null)
|
||||||
|
$message=$source->translate($category,$message,$language);
|
||||||
|
}
|
||||||
|
if($params===array())
|
||||||
|
return $message;
|
||||||
|
if(!is_array($params))
|
||||||
|
$params=array($params);
|
||||||
|
if(isset($params[0])) // number choice
|
||||||
|
{
|
||||||
|
if(strpos($message,'|')!==false)
|
||||||
|
{
|
||||||
|
if(strpos($message,'#')===false)
|
||||||
|
{
|
||||||
|
$chunks=explode('|',$message);
|
||||||
|
$expressions=self::$_app->getLocale($language)->getPluralRules();
|
||||||
|
if($n=min(count($chunks),count($expressions)))
|
||||||
|
{
|
||||||
|
for($i=0;$i<$n;$i++)
|
||||||
|
$chunks[$i]=$expressions[$i].'#'.$chunks[$i];
|
||||||
|
|
||||||
|
$message=implode('|',$chunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$message=CChoiceFormat::format($message,$params[0]);
|
||||||
|
}
|
||||||
|
if(!isset($params['{n}']))
|
||||||
|
$params['{n}']=$params[0];
|
||||||
|
unset($params[0]);
|
||||||
|
}
|
||||||
|
return $params!==array() ? strtr($message,$params) : $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new class autoloader.
|
||||||
|
* The new autoloader will be placed before {@link autoload} and after
|
||||||
|
* any other existing autoloaders.
|
||||||
|
* @param callback $callback a valid PHP callback (function name or array($className,$methodName)).
|
||||||
|
* @param boolean $append whether to append the new autoloader after the default Yii autoloader.
|
||||||
|
* Be careful using this option as it will disable {@link enableIncludePath autoloading via include path}
|
||||||
|
* when set to true. After this the Yii autoloader can not rely on loading classes via simple include anymore
|
||||||
|
* and you have to {@link import} all classes explicitly.
|
||||||
|
*/
|
||||||
|
public static function registerAutoloader($callback, $append=false)
|
||||||
|
{
|
||||||
|
if($append)
|
||||||
|
{
|
||||||
|
self::$enableIncludePath=false;
|
||||||
|
spl_autoload_register($callback);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spl_autoload_unregister(array('YiiBase','autoload'));
|
||||||
|
spl_autoload_register($callback);
|
||||||
|
spl_autoload_register(array('YiiBase','autoload'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array class map for core Yii classes.
|
||||||
|
* NOTE, DO NOT MODIFY THIS ARRAY MANUALLY. IF YOU CHANGE OR ADD SOME CORE CLASSES,
|
||||||
|
* PLEASE RUN 'build autoload' COMMAND TO UPDATE THIS ARRAY.
|
||||||
|
*/
|
||||||
|
private static $_coreClasses=array(
|
||||||
|
'CApplication' => '/base/CApplication.php',
|
||||||
|
'CApplicationComponent' => '/base/CApplicationComponent.php',
|
||||||
|
'CBehavior' => '/base/CBehavior.php',
|
||||||
|
'CComponent' => '/base/CComponent.php',
|
||||||
|
'CErrorEvent' => '/base/CErrorEvent.php',
|
||||||
|
'CErrorHandler' => '/base/CErrorHandler.php',
|
||||||
|
'CException' => '/base/CException.php',
|
||||||
|
'CExceptionEvent' => '/base/CExceptionEvent.php',
|
||||||
|
'CHttpException' => '/base/CHttpException.php',
|
||||||
|
'CModel' => '/base/CModel.php',
|
||||||
|
'CModelBehavior' => '/base/CModelBehavior.php',
|
||||||
|
'CModelEvent' => '/base/CModelEvent.php',
|
||||||
|
'CModule' => '/base/CModule.php',
|
||||||
|
'CSecurityManager' => '/base/CSecurityManager.php',
|
||||||
|
'CStatePersister' => '/base/CStatePersister.php',
|
||||||
|
'CApcCache' => '/caching/CApcCache.php',
|
||||||
|
'CCache' => '/caching/CCache.php',
|
||||||
|
'CDbCache' => '/caching/CDbCache.php',
|
||||||
|
'CDummyCache' => '/caching/CDummyCache.php',
|
||||||
|
'CEAcceleratorCache' => '/caching/CEAcceleratorCache.php',
|
||||||
|
'CFileCache' => '/caching/CFileCache.php',
|
||||||
|
'CMemCache' => '/caching/CMemCache.php',
|
||||||
|
'CRedisCache' => '/caching/CRedisCache.php',
|
||||||
|
'CWinCache' => '/caching/CWinCache.php',
|
||||||
|
'CXCache' => '/caching/CXCache.php',
|
||||||
|
'CZendDataCache' => '/caching/CZendDataCache.php',
|
||||||
|
'CCacheDependency' => '/caching/dependencies/CCacheDependency.php',
|
||||||
|
'CChainedCacheDependency' => '/caching/dependencies/CChainedCacheDependency.php',
|
||||||
|
'CDbCacheDependency' => '/caching/dependencies/CDbCacheDependency.php',
|
||||||
|
'CDirectoryCacheDependency' => '/caching/dependencies/CDirectoryCacheDependency.php',
|
||||||
|
'CExpressionDependency' => '/caching/dependencies/CExpressionDependency.php',
|
||||||
|
'CFileCacheDependency' => '/caching/dependencies/CFileCacheDependency.php',
|
||||||
|
'CGlobalStateCacheDependency' => '/caching/dependencies/CGlobalStateCacheDependency.php',
|
||||||
|
'CAttributeCollection' => '/collections/CAttributeCollection.php',
|
||||||
|
'CConfiguration' => '/collections/CConfiguration.php',
|
||||||
|
'CList' => '/collections/CList.php',
|
||||||
|
'CListIterator' => '/collections/CListIterator.php',
|
||||||
|
'CMap' => '/collections/CMap.php',
|
||||||
|
'CMapIterator' => '/collections/CMapIterator.php',
|
||||||
|
'CQueue' => '/collections/CQueue.php',
|
||||||
|
'CQueueIterator' => '/collections/CQueueIterator.php',
|
||||||
|
'CStack' => '/collections/CStack.php',
|
||||||
|
'CStackIterator' => '/collections/CStackIterator.php',
|
||||||
|
'CTypedList' => '/collections/CTypedList.php',
|
||||||
|
'CTypedMap' => '/collections/CTypedMap.php',
|
||||||
|
'CConsoleApplication' => '/console/CConsoleApplication.php',
|
||||||
|
'CConsoleCommand' => '/console/CConsoleCommand.php',
|
||||||
|
'CConsoleCommandBehavior' => '/console/CConsoleCommandBehavior.php',
|
||||||
|
'CConsoleCommandEvent' => '/console/CConsoleCommandEvent.php',
|
||||||
|
'CConsoleCommandRunner' => '/console/CConsoleCommandRunner.php',
|
||||||
|
'CHelpCommand' => '/console/CHelpCommand.php',
|
||||||
|
'CDbCommand' => '/db/CDbCommand.php',
|
||||||
|
'CDbConnection' => '/db/CDbConnection.php',
|
||||||
|
'CDbDataReader' => '/db/CDbDataReader.php',
|
||||||
|
'CDbException' => '/db/CDbException.php',
|
||||||
|
'CDbMigration' => '/db/CDbMigration.php',
|
||||||
|
'CDbTransaction' => '/db/CDbTransaction.php',
|
||||||
|
'CActiveFinder' => '/db/ar/CActiveFinder.php',
|
||||||
|
'CActiveRecord' => '/db/ar/CActiveRecord.php',
|
||||||
|
'CActiveRecordBehavior' => '/db/ar/CActiveRecordBehavior.php',
|
||||||
|
'CDbColumnSchema' => '/db/schema/CDbColumnSchema.php',
|
||||||
|
'CDbCommandBuilder' => '/db/schema/CDbCommandBuilder.php',
|
||||||
|
'CDbCriteria' => '/db/schema/CDbCriteria.php',
|
||||||
|
'CDbExpression' => '/db/schema/CDbExpression.php',
|
||||||
|
'CDbSchema' => '/db/schema/CDbSchema.php',
|
||||||
|
'CDbTableSchema' => '/db/schema/CDbTableSchema.php',
|
||||||
|
'CMssqlColumnSchema' => '/db/schema/mssql/CMssqlColumnSchema.php',
|
||||||
|
'CMssqlCommandBuilder' => '/db/schema/mssql/CMssqlCommandBuilder.php',
|
||||||
|
'CMssqlPdoAdapter' => '/db/schema/mssql/CMssqlPdoAdapter.php',
|
||||||
|
'CMssqlSchema' => '/db/schema/mssql/CMssqlSchema.php',
|
||||||
|
'CMssqlSqlsrvPdoAdapter' => '/db/schema/mssql/CMssqlSqlsrvPdoAdapter.php',
|
||||||
|
'CMssqlTableSchema' => '/db/schema/mssql/CMssqlTableSchema.php',
|
||||||
|
'CMysqlColumnSchema' => '/db/schema/mysql/CMysqlColumnSchema.php',
|
||||||
|
'CMysqlCommandBuilder' => '/db/schema/mysql/CMysqlCommandBuilder.php',
|
||||||
|
'CMysqlSchema' => '/db/schema/mysql/CMysqlSchema.php',
|
||||||
|
'CMysqlTableSchema' => '/db/schema/mysql/CMysqlTableSchema.php',
|
||||||
|
'COciColumnSchema' => '/db/schema/oci/COciColumnSchema.php',
|
||||||
|
'COciCommandBuilder' => '/db/schema/oci/COciCommandBuilder.php',
|
||||||
|
'COciSchema' => '/db/schema/oci/COciSchema.php',
|
||||||
|
'COciTableSchema' => '/db/schema/oci/COciTableSchema.php',
|
||||||
|
'CPgsqlColumnSchema' => '/db/schema/pgsql/CPgsqlColumnSchema.php',
|
||||||
|
'CPgsqlCommandBuilder' => '/db/schema/pgsql/CPgsqlCommandBuilder.php',
|
||||||
|
'CPgsqlSchema' => '/db/schema/pgsql/CPgsqlSchema.php',
|
||||||
|
'CPgsqlTableSchema' => '/db/schema/pgsql/CPgsqlTableSchema.php',
|
||||||
|
'CSqliteColumnSchema' => '/db/schema/sqlite/CSqliteColumnSchema.php',
|
||||||
|
'CSqliteCommandBuilder' => '/db/schema/sqlite/CSqliteCommandBuilder.php',
|
||||||
|
'CSqliteSchema' => '/db/schema/sqlite/CSqliteSchema.php',
|
||||||
|
'CChoiceFormat' => '/i18n/CChoiceFormat.php',
|
||||||
|
'CDateFormatter' => '/i18n/CDateFormatter.php',
|
||||||
|
'CDbMessageSource' => '/i18n/CDbMessageSource.php',
|
||||||
|
'CGettextMessageSource' => '/i18n/CGettextMessageSource.php',
|
||||||
|
'CLocale' => '/i18n/CLocale.php',
|
||||||
|
'CMessageSource' => '/i18n/CMessageSource.php',
|
||||||
|
'CNumberFormatter' => '/i18n/CNumberFormatter.php',
|
||||||
|
'CPhpMessageSource' => '/i18n/CPhpMessageSource.php',
|
||||||
|
'CGettextFile' => '/i18n/gettext/CGettextFile.php',
|
||||||
|
'CGettextMoFile' => '/i18n/gettext/CGettextMoFile.php',
|
||||||
|
'CGettextPoFile' => '/i18n/gettext/CGettextPoFile.php',
|
||||||
|
'CChainedLogFilter' => '/logging/CChainedLogFilter.php',
|
||||||
|
'CDbLogRoute' => '/logging/CDbLogRoute.php',
|
||||||
|
'CEmailLogRoute' => '/logging/CEmailLogRoute.php',
|
||||||
|
'CFileLogRoute' => '/logging/CFileLogRoute.php',
|
||||||
|
'CLogFilter' => '/logging/CLogFilter.php',
|
||||||
|
'CLogRoute' => '/logging/CLogRoute.php',
|
||||||
|
'CLogRouter' => '/logging/CLogRouter.php',
|
||||||
|
'CLogger' => '/logging/CLogger.php',
|
||||||
|
'CProfileLogRoute' => '/logging/CProfileLogRoute.php',
|
||||||
|
'CWebLogRoute' => '/logging/CWebLogRoute.php',
|
||||||
|
'CDateTimeParser' => '/utils/CDateTimeParser.php',
|
||||||
|
'CFileHelper' => '/utils/CFileHelper.php',
|
||||||
|
'CFormatter' => '/utils/CFormatter.php',
|
||||||
|
'CLocalizedFormatter' => '/utils/CLocalizedFormatter.php',
|
||||||
|
'CMarkdownParser' => '/utils/CMarkdownParser.php',
|
||||||
|
'CPasswordHelper' => '/utils/CPasswordHelper.php',
|
||||||
|
'CPropertyValue' => '/utils/CPropertyValue.php',
|
||||||
|
'CTimestamp' => '/utils/CTimestamp.php',
|
||||||
|
'CVarDumper' => '/utils/CVarDumper.php',
|
||||||
|
'CBooleanValidator' => '/validators/CBooleanValidator.php',
|
||||||
|
'CCaptchaValidator' => '/validators/CCaptchaValidator.php',
|
||||||
|
'CCompareValidator' => '/validators/CCompareValidator.php',
|
||||||
|
'CDateValidator' => '/validators/CDateValidator.php',
|
||||||
|
'CDefaultValueValidator' => '/validators/CDefaultValueValidator.php',
|
||||||
|
'CEmailValidator' => '/validators/CEmailValidator.php',
|
||||||
|
'CExistValidator' => '/validators/CExistValidator.php',
|
||||||
|
'CFileValidator' => '/validators/CFileValidator.php',
|
||||||
|
'CFilterValidator' => '/validators/CFilterValidator.php',
|
||||||
|
'CInlineValidator' => '/validators/CInlineValidator.php',
|
||||||
|
'CNumberValidator' => '/validators/CNumberValidator.php',
|
||||||
|
'CRangeValidator' => '/validators/CRangeValidator.php',
|
||||||
|
'CRegularExpressionValidator' => '/validators/CRegularExpressionValidator.php',
|
||||||
|
'CRequiredValidator' => '/validators/CRequiredValidator.php',
|
||||||
|
'CSafeValidator' => '/validators/CSafeValidator.php',
|
||||||
|
'CStringValidator' => '/validators/CStringValidator.php',
|
||||||
|
'CTypeValidator' => '/validators/CTypeValidator.php',
|
||||||
|
'CUniqueValidator' => '/validators/CUniqueValidator.php',
|
||||||
|
'CUnsafeValidator' => '/validators/CUnsafeValidator.php',
|
||||||
|
'CUrlValidator' => '/validators/CUrlValidator.php',
|
||||||
|
'CValidator' => '/validators/CValidator.php',
|
||||||
|
'CActiveDataProvider' => '/web/CActiveDataProvider.php',
|
||||||
|
'CArrayDataProvider' => '/web/CArrayDataProvider.php',
|
||||||
|
'CAssetManager' => '/web/CAssetManager.php',
|
||||||
|
'CBaseController' => '/web/CBaseController.php',
|
||||||
|
'CCacheHttpSession' => '/web/CCacheHttpSession.php',
|
||||||
|
'CClientScript' => '/web/CClientScript.php',
|
||||||
|
'CController' => '/web/CController.php',
|
||||||
|
'CDataProvider' => '/web/CDataProvider.php',
|
||||||
|
'CDataProviderIterator' => '/web/CDataProviderIterator.php',
|
||||||
|
'CDbHttpSession' => '/web/CDbHttpSession.php',
|
||||||
|
'CExtController' => '/web/CExtController.php',
|
||||||
|
'CFormModel' => '/web/CFormModel.php',
|
||||||
|
'CHttpCookie' => '/web/CHttpCookie.php',
|
||||||
|
'CHttpRequest' => '/web/CHttpRequest.php',
|
||||||
|
'CHttpSession' => '/web/CHttpSession.php',
|
||||||
|
'CHttpSessionIterator' => '/web/CHttpSessionIterator.php',
|
||||||
|
'COutputEvent' => '/web/COutputEvent.php',
|
||||||
|
'CPagination' => '/web/CPagination.php',
|
||||||
|
'CSort' => '/web/CSort.php',
|
||||||
|
'CSqlDataProvider' => '/web/CSqlDataProvider.php',
|
||||||
|
'CTheme' => '/web/CTheme.php',
|
||||||
|
'CThemeManager' => '/web/CThemeManager.php',
|
||||||
|
'CUploadedFile' => '/web/CUploadedFile.php',
|
||||||
|
'CUrlManager' => '/web/CUrlManager.php',
|
||||||
|
'CWebApplication' => '/web/CWebApplication.php',
|
||||||
|
'CWebModule' => '/web/CWebModule.php',
|
||||||
|
'CWidgetFactory' => '/web/CWidgetFactory.php',
|
||||||
|
'CAction' => '/web/actions/CAction.php',
|
||||||
|
'CInlineAction' => '/web/actions/CInlineAction.php',
|
||||||
|
'CViewAction' => '/web/actions/CViewAction.php',
|
||||||
|
'CAccessControlFilter' => '/web/auth/CAccessControlFilter.php',
|
||||||
|
'CAuthAssignment' => '/web/auth/CAuthAssignment.php',
|
||||||
|
'CAuthItem' => '/web/auth/CAuthItem.php',
|
||||||
|
'CAuthManager' => '/web/auth/CAuthManager.php',
|
||||||
|
'CBaseUserIdentity' => '/web/auth/CBaseUserIdentity.php',
|
||||||
|
'CDbAuthManager' => '/web/auth/CDbAuthManager.php',
|
||||||
|
'CPhpAuthManager' => '/web/auth/CPhpAuthManager.php',
|
||||||
|
'CUserIdentity' => '/web/auth/CUserIdentity.php',
|
||||||
|
'CWebUser' => '/web/auth/CWebUser.php',
|
||||||
|
'CFilter' => '/web/filters/CFilter.php',
|
||||||
|
'CFilterChain' => '/web/filters/CFilterChain.php',
|
||||||
|
'CHttpCacheFilter' => '/web/filters/CHttpCacheFilter.php',
|
||||||
|
'CInlineFilter' => '/web/filters/CInlineFilter.php',
|
||||||
|
'CForm' => '/web/form/CForm.php',
|
||||||
|
'CFormButtonElement' => '/web/form/CFormButtonElement.php',
|
||||||
|
'CFormElement' => '/web/form/CFormElement.php',
|
||||||
|
'CFormElementCollection' => '/web/form/CFormElementCollection.php',
|
||||||
|
'CFormInputElement' => '/web/form/CFormInputElement.php',
|
||||||
|
'CFormStringElement' => '/web/form/CFormStringElement.php',
|
||||||
|
'CGoogleApi' => '/web/helpers/CGoogleApi.php',
|
||||||
|
'CHtml' => '/web/helpers/CHtml.php',
|
||||||
|
'CJSON' => '/web/helpers/CJSON.php',
|
||||||
|
'CJavaScript' => '/web/helpers/CJavaScript.php',
|
||||||
|
'CJavaScriptExpression' => '/web/helpers/CJavaScriptExpression.php',
|
||||||
|
'CPradoViewRenderer' => '/web/renderers/CPradoViewRenderer.php',
|
||||||
|
'CViewRenderer' => '/web/renderers/CViewRenderer.php',
|
||||||
|
'CWebService' => '/web/services/CWebService.php',
|
||||||
|
'CWebServiceAction' => '/web/services/CWebServiceAction.php',
|
||||||
|
'CWsdlGenerator' => '/web/services/CWsdlGenerator.php',
|
||||||
|
'CActiveForm' => '/web/widgets/CActiveForm.php',
|
||||||
|
'CAutoComplete' => '/web/widgets/CAutoComplete.php',
|
||||||
|
'CClipWidget' => '/web/widgets/CClipWidget.php',
|
||||||
|
'CContentDecorator' => '/web/widgets/CContentDecorator.php',
|
||||||
|
'CFilterWidget' => '/web/widgets/CFilterWidget.php',
|
||||||
|
'CFlexWidget' => '/web/widgets/CFlexWidget.php',
|
||||||
|
'CHtmlPurifier' => '/web/widgets/CHtmlPurifier.php',
|
||||||
|
'CInputWidget' => '/web/widgets/CInputWidget.php',
|
||||||
|
'CMarkdown' => '/web/widgets/CMarkdown.php',
|
||||||
|
'CMaskedTextField' => '/web/widgets/CMaskedTextField.php',
|
||||||
|
'CMultiFileUpload' => '/web/widgets/CMultiFileUpload.php',
|
||||||
|
'COutputCache' => '/web/widgets/COutputCache.php',
|
||||||
|
'COutputProcessor' => '/web/widgets/COutputProcessor.php',
|
||||||
|
'CStarRating' => '/web/widgets/CStarRating.php',
|
||||||
|
'CTabView' => '/web/widgets/CTabView.php',
|
||||||
|
'CTextHighlighter' => '/web/widgets/CTextHighlighter.php',
|
||||||
|
'CTreeView' => '/web/widgets/CTreeView.php',
|
||||||
|
'CWidget' => '/web/widgets/CWidget.php',
|
||||||
|
'CCaptcha' => '/web/widgets/captcha/CCaptcha.php',
|
||||||
|
'CCaptchaAction' => '/web/widgets/captcha/CCaptchaAction.php',
|
||||||
|
'CBasePager' => '/web/widgets/pagers/CBasePager.php',
|
||||||
|
'CLinkPager' => '/web/widgets/pagers/CLinkPager.php',
|
||||||
|
'CListPager' => '/web/widgets/pagers/CListPager.php',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_autoload_register(array('YiiBase','autoload'));
|
||||||
|
require(YII_PATH.'/base/interfaces.php');
|
990
framework/base/CApplication.php
Normal file
990
framework/base/CApplication.php
Normal file
@ -0,0 +1,990 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CApplication class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CApplication is the base class for all application classes.
|
||||||
|
*
|
||||||
|
* An application serves as the global context that the user request
|
||||||
|
* is being processed. It manages a set of application components that
|
||||||
|
* provide specific functionalities to the whole application.
|
||||||
|
*
|
||||||
|
* The core application components provided by CApplication are the following:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link getErrorHandler errorHandler}: handles PHP errors and
|
||||||
|
* uncaught exceptions. This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getSecurityManager securityManager}: provides security-related
|
||||||
|
* services, such as hashing, encryption. This application component is dynamically
|
||||||
|
* loaded when needed.</li>
|
||||||
|
* <li>{@link getStatePersister statePersister}: provides global state
|
||||||
|
* persistence method. This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getCache cache}: provides caching feature. This application component is
|
||||||
|
* disabled by default.</li>
|
||||||
|
* <li>{@link getMessages messages}: provides the message source for translating
|
||||||
|
* application messages. This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getCoreMessages coreMessages}: provides the message source for translating
|
||||||
|
* Yii framework messages. This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getUrlManager urlManager}: provides URL construction as well as parsing functionality.
|
||||||
|
* This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getRequest request}: represents the current HTTP request by encapsulating
|
||||||
|
* the $_SERVER variable and managing cookies sent from and sent to the user.
|
||||||
|
* This application component is dynamically loaded when needed.</li>
|
||||||
|
* <li>{@link getFormat format}: provides a set of commonly used data formatting methods.
|
||||||
|
* This application component is dynamically loaded when needed.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* CApplication will undergo the following lifecycles when processing a user request:
|
||||||
|
* <ol>
|
||||||
|
* <li>load application configuration;</li>
|
||||||
|
* <li>set up class autoloader and error handling;</li>
|
||||||
|
* <li>load static application components;</li>
|
||||||
|
* <li>{@link onBeginRequest}: preprocess the user request;</li>
|
||||||
|
* <li>{@link processRequest}: process the user request;</li>
|
||||||
|
* <li>{@link onEndRequest}: postprocess the user request;</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* Starting from lifecycle 3, if a PHP error or an uncaught exception occurs,
|
||||||
|
* the application will switch to its error handling logic and jump to step 6 afterwards.
|
||||||
|
*
|
||||||
|
* @property string $id The unique identifier for the application.
|
||||||
|
* @property string $basePath The root directory of the application. Defaults to 'protected'.
|
||||||
|
* @property string $runtimePath The directory that stores runtime files. Defaults to 'protected/runtime'.
|
||||||
|
* @property string $extensionPath The directory that contains all extensions. Defaults to the 'extensions' directory under 'protected'.
|
||||||
|
* @property string $language The language that the user is using and the application should be targeted to.
|
||||||
|
* Defaults to the {@link sourceLanguage source language}.
|
||||||
|
* @property string $timeZone The time zone used by this application.
|
||||||
|
* @property CLocale $locale The locale instance.
|
||||||
|
* @property string $localeDataPath The directory that contains the locale data. It defaults to 'framework/i18n/data'.
|
||||||
|
* @property CNumberFormatter $numberFormatter The locale-dependent number formatter.
|
||||||
|
* The current {@link getLocale application locale} will be used.
|
||||||
|
* @property CDateFormatter $dateFormatter The locale-dependent date formatter.
|
||||||
|
* The current {@link getLocale application locale} will be used.
|
||||||
|
* @property CDbConnection $db The database connection.
|
||||||
|
* @property CErrorHandler $errorHandler The error handler application component.
|
||||||
|
* @property CSecurityManager $securityManager The security manager application component.
|
||||||
|
* @property CStatePersister $statePersister The state persister application component.
|
||||||
|
* @property CCache $cache The cache application component. Null if the component is not enabled.
|
||||||
|
* @property CPhpMessageSource $coreMessages The core message translations.
|
||||||
|
* @property CMessageSource $messages The application message translations.
|
||||||
|
* @property CHttpRequest $request The request component.
|
||||||
|
* @property CUrlManager $urlManager The URL manager component.
|
||||||
|
* @property CController $controller The currently active controller. Null is returned in this base class.
|
||||||
|
* @property string $baseUrl The relative URL for the application.
|
||||||
|
* @property string $homeUrl The homepage URL.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
abstract class CApplication extends CModule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the application name. Defaults to 'My Application'.
|
||||||
|
*/
|
||||||
|
public $name='My Application';
|
||||||
|
/**
|
||||||
|
* @var string the charset currently used for the application. Defaults to 'UTF-8'.
|
||||||
|
*/
|
||||||
|
public $charset='UTF-8';
|
||||||
|
/**
|
||||||
|
* @var string the language that the application is written in. This mainly refers to
|
||||||
|
* the language that the messages and view files are in. Defaults to 'en_us' (US English).
|
||||||
|
*/
|
||||||
|
public $sourceLanguage='en_us';
|
||||||
|
|
||||||
|
private $_id;
|
||||||
|
private $_basePath;
|
||||||
|
private $_runtimePath;
|
||||||
|
private $_extensionPath;
|
||||||
|
private $_globalState;
|
||||||
|
private $_stateChanged;
|
||||||
|
private $_ended=false;
|
||||||
|
private $_language;
|
||||||
|
private $_homeUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the request.
|
||||||
|
* This is the place where the actual request processing work is done.
|
||||||
|
* Derived classes should override this method.
|
||||||
|
*/
|
||||||
|
abstract public function processRequest();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param mixed $config application configuration.
|
||||||
|
* If a string, it is treated as the path of the file that contains the configuration;
|
||||||
|
* If an array, it is the actual configuration information.
|
||||||
|
* Please make sure you specify the {@link getBasePath basePath} property in the configuration,
|
||||||
|
* which should point to the directory containing all application logic, template and data.
|
||||||
|
* If not, the directory will be defaulted to 'protected'.
|
||||||
|
*/
|
||||||
|
public function __construct($config=null)
|
||||||
|
{
|
||||||
|
Yii::setApplication($this);
|
||||||
|
|
||||||
|
// set basePath at early as possible to avoid trouble
|
||||||
|
if(is_string($config))
|
||||||
|
$config=require($config);
|
||||||
|
if(isset($config['basePath']))
|
||||||
|
{
|
||||||
|
$this->setBasePath($config['basePath']);
|
||||||
|
unset($config['basePath']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$this->setBasePath('protected');
|
||||||
|
Yii::setPathOfAlias('application',$this->getBasePath());
|
||||||
|
Yii::setPathOfAlias('webroot',dirname($_SERVER['SCRIPT_FILENAME']));
|
||||||
|
if(isset($config['extensionPath']))
|
||||||
|
{
|
||||||
|
$this->setExtensionPath($config['extensionPath']);
|
||||||
|
unset($config['extensionPath']);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Yii::setPathOfAlias('ext',$this->getBasePath().DIRECTORY_SEPARATOR.'extensions');
|
||||||
|
if(isset($config['aliases']))
|
||||||
|
{
|
||||||
|
$this->setAliases($config['aliases']);
|
||||||
|
unset($config['aliases']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->preinit();
|
||||||
|
|
||||||
|
$this->initSystemHandlers();
|
||||||
|
$this->registerCoreComponents();
|
||||||
|
|
||||||
|
$this->configure($config);
|
||||||
|
$this->attachBehaviors($this->behaviors);
|
||||||
|
$this->preloadComponents();
|
||||||
|
|
||||||
|
$this->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the application.
|
||||||
|
* This method loads static application components. Derived classes usually overrides this
|
||||||
|
* method to do more application-specific tasks.
|
||||||
|
* Remember to call the parent implementation so that static application components are loaded.
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
if($this->hasEventHandler('onBeginRequest'))
|
||||||
|
$this->onBeginRequest(new CEvent($this));
|
||||||
|
register_shutdown_function(array($this,'end'),0,false);
|
||||||
|
$this->processRequest();
|
||||||
|
if($this->hasEventHandler('onEndRequest'))
|
||||||
|
$this->onEndRequest(new CEvent($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminates the application.
|
||||||
|
* This method replaces PHP's exit() function by calling
|
||||||
|
* {@link onEndRequest} before exiting.
|
||||||
|
* @param integer $status exit status (value 0 means normal exit while other values mean abnormal exit).
|
||||||
|
* @param boolean $exit whether to exit the current request. This parameter has been available since version 1.1.5.
|
||||||
|
* It defaults to true, meaning the PHP's exit() function will be called at the end of this method.
|
||||||
|
*/
|
||||||
|
public function end($status=0,$exit=true)
|
||||||
|
{
|
||||||
|
if($this->hasEventHandler('onEndRequest'))
|
||||||
|
$this->onEndRequest(new CEvent($this));
|
||||||
|
if($exit)
|
||||||
|
exit($status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised right BEFORE the application processes the request.
|
||||||
|
* @param CEvent $event the event parameter
|
||||||
|
*/
|
||||||
|
public function onBeginRequest($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onBeginRequest',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised right AFTER the application processes the request.
|
||||||
|
* @param CEvent $event the event parameter
|
||||||
|
*/
|
||||||
|
public function onEndRequest($event)
|
||||||
|
{
|
||||||
|
if(!$this->_ended)
|
||||||
|
{
|
||||||
|
$this->_ended=true;
|
||||||
|
$this->raiseEvent('onEndRequest',$event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unique identifier for the application.
|
||||||
|
* @return string the unique identifier for the application.
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
if($this->_id!==null)
|
||||||
|
return $this->_id;
|
||||||
|
else
|
||||||
|
return $this->_id=sprintf('%x',crc32($this->getBasePath().$this->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the unique identifier for the application.
|
||||||
|
* @param string $id the unique identifier for the application.
|
||||||
|
*/
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->_id=$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root path of the application.
|
||||||
|
* @return string the root directory of the application. Defaults to 'protected'.
|
||||||
|
*/
|
||||||
|
public function getBasePath()
|
||||||
|
{
|
||||||
|
return $this->_basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the root directory of the application.
|
||||||
|
* This method can only be invoked at the begin of the constructor.
|
||||||
|
* @param string $path the root directory of the application.
|
||||||
|
* @throws CException if the directory does not exist.
|
||||||
|
*/
|
||||||
|
public function setBasePath($path)
|
||||||
|
{
|
||||||
|
if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath))
|
||||||
|
throw new CException(Yii::t('yii','Application base path "{path}" is not a valid directory.',
|
||||||
|
array('{path}'=>$path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the directory that stores runtime files.
|
||||||
|
* @return string the directory that stores runtime files. Defaults to 'protected/runtime'.
|
||||||
|
*/
|
||||||
|
public function getRuntimePath()
|
||||||
|
{
|
||||||
|
if($this->_runtimePath!==null)
|
||||||
|
return $this->_runtimePath;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->setRuntimePath($this->getBasePath().DIRECTORY_SEPARATOR.'runtime');
|
||||||
|
return $this->_runtimePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the directory that stores runtime files.
|
||||||
|
* @param string $path the directory that stores runtime files.
|
||||||
|
* @throws CException if the directory does not exist or is not writable
|
||||||
|
*/
|
||||||
|
public function setRuntimePath($path)
|
||||||
|
{
|
||||||
|
if(($runtimePath=realpath($path))===false || !is_dir($runtimePath) || !is_writable($runtimePath))
|
||||||
|
throw new CException(Yii::t('yii','Application runtime path "{path}" is not valid. Please make sure it is a directory writable by the Web server process.',
|
||||||
|
array('{path}'=>$path)));
|
||||||
|
$this->_runtimePath=$runtimePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root directory that holds all third-party extensions.
|
||||||
|
* @return string the directory that contains all extensions. Defaults to the 'extensions' directory under 'protected'.
|
||||||
|
*/
|
||||||
|
public function getExtensionPath()
|
||||||
|
{
|
||||||
|
return Yii::getPathOfAlias('ext');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the root directory that holds all third-party extensions.
|
||||||
|
* @param string $path the directory that contains all third-party extensions.
|
||||||
|
* @throws CException if the directory does not exist
|
||||||
|
*/
|
||||||
|
public function setExtensionPath($path)
|
||||||
|
{
|
||||||
|
if(($extensionPath=realpath($path))===false || !is_dir($extensionPath))
|
||||||
|
throw new CException(Yii::t('yii','Extension path "{path}" does not exist.',
|
||||||
|
array('{path}'=>$path)));
|
||||||
|
Yii::setPathOfAlias('ext',$extensionPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the language that the user is using and the application should be targeted to.
|
||||||
|
* @return string the language that the user is using and the application should be targeted to.
|
||||||
|
* Defaults to the {@link sourceLanguage source language}.
|
||||||
|
*/
|
||||||
|
public function getLanguage()
|
||||||
|
{
|
||||||
|
return $this->_language===null ? $this->sourceLanguage : $this->_language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies which language the application is targeted to.
|
||||||
|
*
|
||||||
|
* This is the language that the application displays to end users.
|
||||||
|
* If set null, it uses the {@link sourceLanguage source language}.
|
||||||
|
*
|
||||||
|
* Unless your application needs to support multiple languages, you should always
|
||||||
|
* set this language to null to maximize the application's performance.
|
||||||
|
* @param string $language the user language (e.g. 'en_US', 'zh_CN').
|
||||||
|
* If it is null, the {@link sourceLanguage} will be used.
|
||||||
|
*/
|
||||||
|
public function setLanguage($language)
|
||||||
|
{
|
||||||
|
$this->_language=$language;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time zone used by this application.
|
||||||
|
* This is a simple wrapper of PHP function date_default_timezone_get().
|
||||||
|
* @return string the time zone used by this application.
|
||||||
|
* @see http://php.net/manual/en/function.date-default-timezone-get.php
|
||||||
|
*/
|
||||||
|
public function getTimeZone()
|
||||||
|
{
|
||||||
|
return date_default_timezone_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the time zone used by this application.
|
||||||
|
* This is a simple wrapper of PHP function date_default_timezone_set().
|
||||||
|
* @param string $value the time zone used by this application.
|
||||||
|
* @see http://php.net/manual/en/function.date-default-timezone-set.php
|
||||||
|
*/
|
||||||
|
public function setTimeZone($value)
|
||||||
|
{
|
||||||
|
date_default_timezone_set($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the localized version of a specified file.
|
||||||
|
*
|
||||||
|
* The searching is based on the specified language code. In particular,
|
||||||
|
* a file with the same name will be looked for under the subdirectory
|
||||||
|
* named as the locale ID. For example, given the file "path/to/view.php"
|
||||||
|
* and locale ID "zh_cn", the localized file will be looked for as
|
||||||
|
* "path/to/zh_cn/view.php". If the file is not found, the original file
|
||||||
|
* will be returned.
|
||||||
|
*
|
||||||
|
* For consistency, it is recommended that the locale ID is given
|
||||||
|
* in lower case and in the format of LanguageID_RegionID (e.g. "en_us").
|
||||||
|
*
|
||||||
|
* @param string $srcFile the original file
|
||||||
|
* @param string $srcLanguage the language that the original file is in. If null, the application {@link sourceLanguage source language} is used.
|
||||||
|
* @param string $language the desired language that the file should be localized to. If null, the {@link getLanguage application language} will be used.
|
||||||
|
* @return string the matching localized file. The original file is returned if no localized version is found
|
||||||
|
* or if source language is the same as the desired language.
|
||||||
|
*/
|
||||||
|
public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null)
|
||||||
|
{
|
||||||
|
if($srcLanguage===null)
|
||||||
|
$srcLanguage=$this->sourceLanguage;
|
||||||
|
if($language===null)
|
||||||
|
$language=$this->getLanguage();
|
||||||
|
if($language===$srcLanguage)
|
||||||
|
return $srcFile;
|
||||||
|
$desiredFile=dirname($srcFile).DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.basename($srcFile);
|
||||||
|
return is_file($desiredFile) ? $desiredFile : $srcFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale instance.
|
||||||
|
* @param string $localeID the locale ID (e.g. en_US). If null, the {@link getLanguage application language ID} will be used.
|
||||||
|
* @return CLocale the locale instance
|
||||||
|
*/
|
||||||
|
public function getLocale($localeID=null)
|
||||||
|
{
|
||||||
|
return CLocale::getInstance($localeID===null?$this->getLanguage():$localeID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the directory that contains the locale data.
|
||||||
|
* @return string the directory that contains the locale data. It defaults to 'framework/i18n/data'.
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public function getLocaleDataPath()
|
||||||
|
{
|
||||||
|
return CLocale::$dataPath===null ? Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the directory that contains the locale data.
|
||||||
|
* @param string $value the directory that contains the locale data.
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public function setLocaleDataPath($value)
|
||||||
|
{
|
||||||
|
CLocale::$dataPath=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CNumberFormatter the locale-dependent number formatter.
|
||||||
|
* The current {@link getLocale application locale} will be used.
|
||||||
|
*/
|
||||||
|
public function getNumberFormatter()
|
||||||
|
{
|
||||||
|
return $this->getLocale()->getNumberFormatter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale-dependent date formatter.
|
||||||
|
* @return CDateFormatter the locale-dependent date formatter.
|
||||||
|
* The current {@link getLocale application locale} will be used.
|
||||||
|
*/
|
||||||
|
public function getDateFormatter()
|
||||||
|
{
|
||||||
|
return $this->getLocale()->getDateFormatter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the database connection component.
|
||||||
|
* @return CDbConnection the database connection
|
||||||
|
*/
|
||||||
|
public function getDb()
|
||||||
|
{
|
||||||
|
return $this->getComponent('db');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error handler component.
|
||||||
|
* @return CErrorHandler the error handler application component.
|
||||||
|
*/
|
||||||
|
public function getErrorHandler()
|
||||||
|
{
|
||||||
|
return $this->getComponent('errorHandler');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the security manager component.
|
||||||
|
* @return CSecurityManager the security manager application component.
|
||||||
|
*/
|
||||||
|
public function getSecurityManager()
|
||||||
|
{
|
||||||
|
return $this->getComponent('securityManager');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the state persister component.
|
||||||
|
* @return CStatePersister the state persister application component.
|
||||||
|
*/
|
||||||
|
public function getStatePersister()
|
||||||
|
{
|
||||||
|
return $this->getComponent('statePersister');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache component.
|
||||||
|
* @return CCache the cache application component. Null if the component is not enabled.
|
||||||
|
*/
|
||||||
|
public function getCache()
|
||||||
|
{
|
||||||
|
return $this->getComponent('cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the core message translations component.
|
||||||
|
* @return CPhpMessageSource the core message translations
|
||||||
|
*/
|
||||||
|
public function getCoreMessages()
|
||||||
|
{
|
||||||
|
return $this->getComponent('coreMessages');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application message translations component.
|
||||||
|
* @return CMessageSource the application message translations
|
||||||
|
*/
|
||||||
|
public function getMessages()
|
||||||
|
{
|
||||||
|
return $this->getComponent('messages');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the request component.
|
||||||
|
* @return CHttpRequest the request component
|
||||||
|
*/
|
||||||
|
public function getRequest()
|
||||||
|
{
|
||||||
|
return $this->getComponent('request');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the URL manager component.
|
||||||
|
* @return CUrlManager the URL manager component
|
||||||
|
*/
|
||||||
|
public function getUrlManager()
|
||||||
|
{
|
||||||
|
return $this->getComponent('urlManager');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CController the currently active controller. Null is returned in this base class.
|
||||||
|
* @since 1.1.8
|
||||||
|
*/
|
||||||
|
public function getController()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a relative URL based on the given controller and action information.
|
||||||
|
* @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
|
||||||
|
* @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
|
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL.
|
||||||
|
* @return string the constructed URL
|
||||||
|
*/
|
||||||
|
public function createUrl($route,$params=array(),$ampersand='&')
|
||||||
|
{
|
||||||
|
return $this->getUrlManager()->createUrl($route,$params,$ampersand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an absolute URL based on the given controller and action information.
|
||||||
|
* @param string $route the URL route. This should be in the format of 'ControllerID/ActionID'.
|
||||||
|
* @param array $params additional GET parameters (name=>value). Both the name and value will be URL-encoded.
|
||||||
|
* @param string $schema schema to use (e.g. http, https). If empty, the schema used for the current request will be used.
|
||||||
|
* @param string $ampersand the token separating name-value pairs in the URL.
|
||||||
|
* @return string the constructed URL
|
||||||
|
*/
|
||||||
|
public function createAbsoluteUrl($route,$params=array(),$schema='',$ampersand='&')
|
||||||
|
{
|
||||||
|
$url=$this->createUrl($route,$params,$ampersand);
|
||||||
|
if(strpos($url,'http')===0)
|
||||||
|
return $url;
|
||||||
|
else
|
||||||
|
return $this->getRequest()->getHostInfo($schema).$url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the relative URL for the application.
|
||||||
|
* This is a shortcut method to {@link CHttpRequest::getBaseUrl()}.
|
||||||
|
* @param boolean $absolute whether to return an absolute URL. Defaults to false, meaning returning a relative one.
|
||||||
|
* @return string the relative URL for the application
|
||||||
|
* @see CHttpRequest::getBaseUrl()
|
||||||
|
*/
|
||||||
|
public function getBaseUrl($absolute=false)
|
||||||
|
{
|
||||||
|
return $this->getRequest()->getBaseUrl($absolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the homepage URL
|
||||||
|
*/
|
||||||
|
public function getHomeUrl()
|
||||||
|
{
|
||||||
|
if($this->_homeUrl===null)
|
||||||
|
{
|
||||||
|
if($this->getUrlManager()->showScriptName)
|
||||||
|
return $this->getRequest()->getScriptUrl();
|
||||||
|
else
|
||||||
|
return $this->getRequest()->getBaseUrl().'/';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $this->_homeUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value the homepage URL
|
||||||
|
*/
|
||||||
|
public function setHomeUrl($value)
|
||||||
|
{
|
||||||
|
$this->_homeUrl=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a global value.
|
||||||
|
*
|
||||||
|
* A global value is one that is persistent across users sessions and requests.
|
||||||
|
* @param string $key the name of the value to be returned
|
||||||
|
* @param mixed $defaultValue the default value. If the named global value is not found, this will be returned instead.
|
||||||
|
* @return mixed the named global value
|
||||||
|
* @see setGlobalState
|
||||||
|
*/
|
||||||
|
public function getGlobalState($key,$defaultValue=null)
|
||||||
|
{
|
||||||
|
if($this->_globalState===null)
|
||||||
|
$this->loadGlobalState();
|
||||||
|
if(isset($this->_globalState[$key]))
|
||||||
|
return $this->_globalState[$key];
|
||||||
|
else
|
||||||
|
return $defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a global value.
|
||||||
|
*
|
||||||
|
* A global value is one that is persistent across users sessions and requests.
|
||||||
|
* Make sure that the value is serializable and unserializable.
|
||||||
|
* @param string $key the name of the value to be saved
|
||||||
|
* @param mixed $value the global value to be saved. It must be serializable.
|
||||||
|
* @param mixed $defaultValue the default value. If the named global value is the same as this value, it will be cleared from the current storage.
|
||||||
|
* @see getGlobalState
|
||||||
|
*/
|
||||||
|
public function setGlobalState($key,$value,$defaultValue=null)
|
||||||
|
{
|
||||||
|
if($this->_globalState===null)
|
||||||
|
$this->loadGlobalState();
|
||||||
|
|
||||||
|
$changed=$this->_stateChanged;
|
||||||
|
if($value===$defaultValue)
|
||||||
|
{
|
||||||
|
if(isset($this->_globalState[$key]))
|
||||||
|
{
|
||||||
|
unset($this->_globalState[$key]);
|
||||||
|
$this->_stateChanged=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif(!isset($this->_globalState[$key]) || $this->_globalState[$key]!==$value)
|
||||||
|
{
|
||||||
|
$this->_globalState[$key]=$value;
|
||||||
|
$this->_stateChanged=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->_stateChanged!==$changed)
|
||||||
|
$this->attachEventHandler('onEndRequest',array($this,'saveGlobalState'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears a global value.
|
||||||
|
*
|
||||||
|
* The value cleared will no longer be available in this request and the following requests.
|
||||||
|
* @param string $key the name of the value to be cleared
|
||||||
|
*/
|
||||||
|
public function clearGlobalState($key)
|
||||||
|
{
|
||||||
|
$this->setGlobalState($key,true,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the global state data from persistent storage.
|
||||||
|
* @see getStatePersister
|
||||||
|
* @throws CException if the state persister is not available
|
||||||
|
*/
|
||||||
|
public function loadGlobalState()
|
||||||
|
{
|
||||||
|
$persister=$this->getStatePersister();
|
||||||
|
if(($this->_globalState=$persister->load())===null)
|
||||||
|
$this->_globalState=array();
|
||||||
|
$this->_stateChanged=false;
|
||||||
|
$this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the global state data into persistent storage.
|
||||||
|
* @see getStatePersister
|
||||||
|
* @throws CException if the state persister is not available
|
||||||
|
*/
|
||||||
|
public function saveGlobalState()
|
||||||
|
{
|
||||||
|
if($this->_stateChanged)
|
||||||
|
{
|
||||||
|
$this->_stateChanged=false;
|
||||||
|
$this->detachEventHandler('onEndRequest',array($this,'saveGlobalState'));
|
||||||
|
$this->getStatePersister()->save($this->_globalState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles uncaught PHP exceptions.
|
||||||
|
*
|
||||||
|
* This method is implemented as a PHP exception handler. It requires
|
||||||
|
* that constant YII_ENABLE_EXCEPTION_HANDLER be defined true.
|
||||||
|
*
|
||||||
|
* This method will first raise an {@link onException} event.
|
||||||
|
* If the exception is not handled by any event handler, it will call
|
||||||
|
* {@link getErrorHandler errorHandler} to process the exception.
|
||||||
|
*
|
||||||
|
* The application will be terminated by this method.
|
||||||
|
*
|
||||||
|
* @param Exception $exception exception that is not caught
|
||||||
|
*/
|
||||||
|
public function handleException($exception)
|
||||||
|
{
|
||||||
|
// disable error capturing to avoid recursive errors
|
||||||
|
restore_error_handler();
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
$category='exception.'.get_class($exception);
|
||||||
|
if($exception instanceof CHttpException)
|
||||||
|
$category.='.'.$exception->statusCode;
|
||||||
|
// php <5.2 doesn't support string conversion auto-magically
|
||||||
|
$message=$exception->__toString();
|
||||||
|
if(isset($_SERVER['REQUEST_URI']))
|
||||||
|
$message.="\nREQUEST_URI=".$_SERVER['REQUEST_URI'];
|
||||||
|
if(isset($_SERVER['HTTP_REFERER']))
|
||||||
|
$message.="\nHTTP_REFERER=".$_SERVER['HTTP_REFERER'];
|
||||||
|
$message.="\n---";
|
||||||
|
Yii::log($message,CLogger::LEVEL_ERROR,$category);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$event=new CExceptionEvent($this,$exception);
|
||||||
|
$this->onException($event);
|
||||||
|
if(!$event->handled)
|
||||||
|
{
|
||||||
|
// try an error handler
|
||||||
|
if(($handler=$this->getErrorHandler())!==null)
|
||||||
|
$handler->handle($event);
|
||||||
|
else
|
||||||
|
$this->displayException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
$this->displayException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->end(1);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
// use the most primitive way to log error
|
||||||
|
$msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
|
||||||
|
$msg .= $e->getTraceAsString()."\n";
|
||||||
|
$msg .= "Previous exception:\n";
|
||||||
|
$msg .= get_class($exception).': '.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().")\n";
|
||||||
|
$msg .= $exception->getTraceAsString()."\n";
|
||||||
|
$msg .= '$_SERVER='.var_export($_SERVER,true);
|
||||||
|
error_log($msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles PHP execution errors such as warnings, notices.
|
||||||
|
*
|
||||||
|
* This method is implemented as a PHP error handler. It requires
|
||||||
|
* that constant YII_ENABLE_ERROR_HANDLER be defined true.
|
||||||
|
*
|
||||||
|
* This method will first raise an {@link onError} event.
|
||||||
|
* If the error is not handled by any event handler, it will call
|
||||||
|
* {@link getErrorHandler errorHandler} to process the error.
|
||||||
|
*
|
||||||
|
* The application will be terminated by this method.
|
||||||
|
*
|
||||||
|
* @param integer $code the level of the error raised
|
||||||
|
* @param string $message the error message
|
||||||
|
* @param string $file the filename that the error was raised in
|
||||||
|
* @param integer $line the line number the error was raised at
|
||||||
|
*/
|
||||||
|
public function handleError($code,$message,$file,$line)
|
||||||
|
{
|
||||||
|
if($code & error_reporting())
|
||||||
|
{
|
||||||
|
// disable error capturing to avoid recursive errors
|
||||||
|
restore_error_handler();
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
$log="$message ($file:$line)\nStack trace:\n";
|
||||||
|
$trace=debug_backtrace();
|
||||||
|
// skip the first 3 stacks as they do not tell the error position
|
||||||
|
if(count($trace)>3)
|
||||||
|
$trace=array_slice($trace,3);
|
||||||
|
foreach($trace as $i=>$t)
|
||||||
|
{
|
||||||
|
if(!isset($t['file']))
|
||||||
|
$t['file']='unknown';
|
||||||
|
if(!isset($t['line']))
|
||||||
|
$t['line']=0;
|
||||||
|
if(!isset($t['function']))
|
||||||
|
$t['function']='unknown';
|
||||||
|
$log.="#$i {$t['file']}({$t['line']}): ";
|
||||||
|
if(isset($t['object']) && is_object($t['object']))
|
||||||
|
$log.=get_class($t['object']).'->';
|
||||||
|
$log.="{$t['function']}()\n";
|
||||||
|
}
|
||||||
|
if(isset($_SERVER['REQUEST_URI']))
|
||||||
|
$log.='REQUEST_URI='.$_SERVER['REQUEST_URI'];
|
||||||
|
Yii::log($log,CLogger::LEVEL_ERROR,'php');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Yii::import('CErrorEvent',true);
|
||||||
|
$event=new CErrorEvent($this,$code,$message,$file,$line);
|
||||||
|
$this->onError($event);
|
||||||
|
if(!$event->handled)
|
||||||
|
{
|
||||||
|
// try an error handler
|
||||||
|
if(($handler=$this->getErrorHandler())!==null)
|
||||||
|
$handler->handle($event);
|
||||||
|
else
|
||||||
|
$this->displayError($code,$message,$file,$line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
$this->displayException($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$this->end(1);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
// use the most primitive way to log error
|
||||||
|
$msg = get_class($e).': '.$e->getMessage().' ('.$e->getFile().':'.$e->getLine().")\n";
|
||||||
|
$msg .= $e->getTraceAsString()."\n";
|
||||||
|
$msg .= "Previous error:\n";
|
||||||
|
$msg .= $log."\n";
|
||||||
|
$msg .= '$_SERVER='.var_export($_SERVER,true);
|
||||||
|
error_log($msg);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised when an uncaught PHP exception occurs.
|
||||||
|
*
|
||||||
|
* An event handler can set the {@link CExceptionEvent::handled handled}
|
||||||
|
* property of the event parameter to be true to indicate no further error
|
||||||
|
* handling is needed. Otherwise, the {@link getErrorHandler errorHandler}
|
||||||
|
* application component will continue processing the error.
|
||||||
|
*
|
||||||
|
* @param CExceptionEvent $event event parameter
|
||||||
|
*/
|
||||||
|
public function onException($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onException',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raised when a PHP execution error occurs.
|
||||||
|
*
|
||||||
|
* An event handler can set the {@link CErrorEvent::handled handled}
|
||||||
|
* property of the event parameter to be true to indicate no further error
|
||||||
|
* handling is needed. Otherwise, the {@link getErrorHandler errorHandler}
|
||||||
|
* application component will continue processing the error.
|
||||||
|
*
|
||||||
|
* @param CErrorEvent $event event parameter
|
||||||
|
*/
|
||||||
|
public function onError($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onError',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the captured PHP error.
|
||||||
|
* This method displays the error in HTML when there is
|
||||||
|
* no active error handler.
|
||||||
|
* @param integer $code error code
|
||||||
|
* @param string $message error message
|
||||||
|
* @param string $file error file
|
||||||
|
* @param string $line error line
|
||||||
|
*/
|
||||||
|
public function displayError($code,$message,$file,$line)
|
||||||
|
{
|
||||||
|
if(YII_DEBUG)
|
||||||
|
{
|
||||||
|
echo "<h1>PHP Error [$code]</h1>\n";
|
||||||
|
echo "<p>$message ($file:$line)</p>\n";
|
||||||
|
echo '<pre>';
|
||||||
|
|
||||||
|
$trace=debug_backtrace();
|
||||||
|
// skip the first 3 stacks as they do not tell the error position
|
||||||
|
if(count($trace)>3)
|
||||||
|
$trace=array_slice($trace,3);
|
||||||
|
foreach($trace as $i=>$t)
|
||||||
|
{
|
||||||
|
if(!isset($t['file']))
|
||||||
|
$t['file']='unknown';
|
||||||
|
if(!isset($t['line']))
|
||||||
|
$t['line']=0;
|
||||||
|
if(!isset($t['function']))
|
||||||
|
$t['function']='unknown';
|
||||||
|
echo "#$i {$t['file']}({$t['line']}): ";
|
||||||
|
if(isset($t['object']) && is_object($t['object']))
|
||||||
|
echo get_class($t['object']).'->';
|
||||||
|
echo "{$t['function']}()\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</pre>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "<h1>PHP Error [$code]</h1>\n";
|
||||||
|
echo "<p>$message</p>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the uncaught PHP exception.
|
||||||
|
* This method displays the exception in HTML when there is
|
||||||
|
* no active error handler.
|
||||||
|
* @param Exception $exception the uncaught exception
|
||||||
|
*/
|
||||||
|
public function displayException($exception)
|
||||||
|
{
|
||||||
|
if(YII_DEBUG)
|
||||||
|
{
|
||||||
|
echo '<h1>'.get_class($exception)."</h1>\n";
|
||||||
|
echo '<p>'.$exception->getMessage().' ('.$exception->getFile().':'.$exception->getLine().')</p>';
|
||||||
|
echo '<pre>'.$exception->getTraceAsString().'</pre>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo '<h1>'.get_class($exception)."</h1>\n";
|
||||||
|
echo '<p>'.$exception->getMessage().'</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the class autoloader and error handlers.
|
||||||
|
*/
|
||||||
|
protected function initSystemHandlers()
|
||||||
|
{
|
||||||
|
if(YII_ENABLE_EXCEPTION_HANDLER)
|
||||||
|
set_exception_handler(array($this,'handleException'));
|
||||||
|
if(YII_ENABLE_ERROR_HANDLER)
|
||||||
|
set_error_handler(array($this,'handleError'),error_reporting());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the core application components.
|
||||||
|
* @see setComponents
|
||||||
|
*/
|
||||||
|
protected function registerCoreComponents()
|
||||||
|
{
|
||||||
|
$components=array(
|
||||||
|
'coreMessages'=>array(
|
||||||
|
'class'=>'CPhpMessageSource',
|
||||||
|
'language'=>'en_us',
|
||||||
|
'basePath'=>YII_PATH.DIRECTORY_SEPARATOR.'messages',
|
||||||
|
),
|
||||||
|
'db'=>array(
|
||||||
|
'class'=>'CDbConnection',
|
||||||
|
),
|
||||||
|
'messages'=>array(
|
||||||
|
'class'=>'CPhpMessageSource',
|
||||||
|
),
|
||||||
|
'errorHandler'=>array(
|
||||||
|
'class'=>'CErrorHandler',
|
||||||
|
),
|
||||||
|
'securityManager'=>array(
|
||||||
|
'class'=>'CSecurityManager',
|
||||||
|
),
|
||||||
|
'statePersister'=>array(
|
||||||
|
'class'=>'CStatePersister',
|
||||||
|
),
|
||||||
|
'urlManager'=>array(
|
||||||
|
'class'=>'CUrlManager',
|
||||||
|
),
|
||||||
|
'request'=>array(
|
||||||
|
'class'=>'CHttpRequest',
|
||||||
|
),
|
||||||
|
'format'=>array(
|
||||||
|
'class'=>'CFormatter',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->setComponents($components);
|
||||||
|
}
|
||||||
|
}
|
57
framework/base/CApplicationComponent.php
Normal file
57
framework/base/CApplicationComponent.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains the base application component class.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CApplicationComponent is the base class for application component classes.
|
||||||
|
*
|
||||||
|
* CApplicationComponent implements the basic methods required by {@link IApplicationComponent}.
|
||||||
|
*
|
||||||
|
* When developing an application component, try to put application component initialization code in
|
||||||
|
* the {@link init()} method instead of the constructor. This has the advantage that
|
||||||
|
* the application component can be customized through application configuration.
|
||||||
|
*
|
||||||
|
* @property boolean $isInitialized Whether this application component has been initialized (ie, {@link init()} is invoked).
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
abstract class CApplicationComponent extends CComponent implements IApplicationComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array the behaviors that should be attached to this component.
|
||||||
|
* The behaviors will be attached to the component when {@link init} is called.
|
||||||
|
* Please refer to {@link CModel::behaviors} on how to specify the value of this property.
|
||||||
|
*/
|
||||||
|
public $behaviors=array();
|
||||||
|
|
||||||
|
private $_initialized=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the application component.
|
||||||
|
* This method is required by {@link IApplicationComponent} and is invoked by application.
|
||||||
|
* If you override this method, make sure to call the parent implementation
|
||||||
|
* so that the application component can be marked as initialized.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
$this->attachBehaviors($this->behaviors);
|
||||||
|
$this->_initialized=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this application component has been initialized.
|
||||||
|
* @return boolean whether this application component has been initialized (ie, {@link init()} is invoked).
|
||||||
|
*/
|
||||||
|
public function getIsInitialized()
|
||||||
|
{
|
||||||
|
return $this->_initialized;
|
||||||
|
}
|
||||||
|
}
|
114
framework/base/CBehavior.php
Normal file
114
framework/base/CBehavior.php
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CBehavior class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CBehavior is a convenient base class for behavior classes.
|
||||||
|
*
|
||||||
|
* @property CComponent $owner The owner component that this behavior is attached to.
|
||||||
|
* @property boolean $enabled Whether this behavior is enabled.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
*/
|
||||||
|
class CBehavior extends CComponent implements IBehavior
|
||||||
|
{
|
||||||
|
private $_enabled=false;
|
||||||
|
private $_owner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares events and the corresponding event handler methods.
|
||||||
|
* The events are defined by the {@link owner} component, while the handler
|
||||||
|
* methods by the behavior class. The handlers will be attached to the corresponding
|
||||||
|
* events when the behavior is attached to the {@link owner} component; and they
|
||||||
|
* will be detached from the events when the behavior is detached from the component.
|
||||||
|
* Make sure you've declared handler method as public.
|
||||||
|
* @return array events (array keys) and the corresponding event handler methods (array values).
|
||||||
|
*/
|
||||||
|
public function events()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the behavior object to the component.
|
||||||
|
* The default implementation will set the {@link owner} property
|
||||||
|
* and attach event handlers as declared in {@link events}.
|
||||||
|
* This method will also set {@link enabled} to true.
|
||||||
|
* Make sure you've declared handler as public and call the parent implementation if you override this method.
|
||||||
|
* @param CComponent $owner the component that this behavior is to be attached to.
|
||||||
|
*/
|
||||||
|
public function attach($owner)
|
||||||
|
{
|
||||||
|
$this->_enabled=true;
|
||||||
|
$this->_owner=$owner;
|
||||||
|
$this->_attachEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches the behavior object from the component.
|
||||||
|
* The default implementation will unset the {@link owner} property
|
||||||
|
* and detach event handlers declared in {@link events}.
|
||||||
|
* This method will also set {@link enabled} to false.
|
||||||
|
* Make sure you call the parent implementation if you override this method.
|
||||||
|
* @param CComponent $owner the component that this behavior is to be detached from.
|
||||||
|
*/
|
||||||
|
public function detach($owner)
|
||||||
|
{
|
||||||
|
foreach($this->events() as $event=>$handler)
|
||||||
|
$owner->detachEventHandler($event,array($this,$handler));
|
||||||
|
$this->_owner=null;
|
||||||
|
$this->_enabled=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CComponent the owner component that this behavior is attached to.
|
||||||
|
*/
|
||||||
|
public function getOwner()
|
||||||
|
{
|
||||||
|
return $this->_owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean whether this behavior is enabled
|
||||||
|
*/
|
||||||
|
public function getEnabled()
|
||||||
|
{
|
||||||
|
return $this->_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param boolean $value whether this behavior is enabled
|
||||||
|
*/
|
||||||
|
public function setEnabled($value)
|
||||||
|
{
|
||||||
|
$value=(bool)$value;
|
||||||
|
if($this->_enabled!=$value && $this->_owner)
|
||||||
|
{
|
||||||
|
if($value)
|
||||||
|
$this->_attachEventHandlers();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($this->events() as $event=>$handler)
|
||||||
|
$this->_owner->detachEventHandler($event,array($this,$handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->_enabled=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _attachEventHandlers()
|
||||||
|
{
|
||||||
|
$class=new ReflectionClass($this);
|
||||||
|
foreach($this->events() as $event=>$handler)
|
||||||
|
{
|
||||||
|
if($class->getMethod($handler)->isPublic())
|
||||||
|
$this->_owner->attachEventHandler($event,array($this,$handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
689
framework/base/CComponent.php
Normal file
689
framework/base/CComponent.php
Normal file
@ -0,0 +1,689 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains the foundation classes for component-based and event-driven programming.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CComponent is the base class for all components.
|
||||||
|
*
|
||||||
|
* CComponent implements the protocol of defining, using properties and events.
|
||||||
|
*
|
||||||
|
* A property is defined by a getter method, and/or a setter method.
|
||||||
|
* Properties can be accessed in the way like accessing normal object members.
|
||||||
|
* Reading or writing a property will cause the invocation of the corresponding
|
||||||
|
* getter or setter method, e.g
|
||||||
|
* <pre>
|
||||||
|
* $a=$component->text; // equivalent to $a=$component->getText();
|
||||||
|
* $component->text='abc'; // equivalent to $component->setText('abc');
|
||||||
|
* </pre>
|
||||||
|
* The signatures of getter and setter methods are as follows,
|
||||||
|
* <pre>
|
||||||
|
* // getter, defines a readable property 'text'
|
||||||
|
* public function getText() { ... }
|
||||||
|
* // setter, defines a writable property 'text' with $value to be set to the property
|
||||||
|
* public function setText($value) { ... }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* An event is defined by the presence of a method whose name starts with 'on'.
|
||||||
|
* The event name is the method name. When an event is raised, functions
|
||||||
|
* (called event handlers) attached to the event will be invoked automatically.
|
||||||
|
*
|
||||||
|
* An event can be raised by calling {@link raiseEvent} method, upon which
|
||||||
|
* the attached event handlers will be invoked automatically in the order they
|
||||||
|
* are attached to the event. Event handlers must have the following signature,
|
||||||
|
* <pre>
|
||||||
|
* function eventHandler($event) { ... }
|
||||||
|
* </pre>
|
||||||
|
* where $event includes parameters associated with the event.
|
||||||
|
*
|
||||||
|
* To attach an event handler to an event, see {@link attachEventHandler}.
|
||||||
|
* You can also use the following syntax:
|
||||||
|
* <pre>
|
||||||
|
* $component->onClick=$callback; // or $component->onClick->add($callback);
|
||||||
|
* </pre>
|
||||||
|
* where $callback refers to a valid PHP callback. Below we show some callback examples:
|
||||||
|
* <pre>
|
||||||
|
* 'handleOnClick' // handleOnClick() is a global function
|
||||||
|
* array($object,'handleOnClick') // using $object->handleOnClick()
|
||||||
|
* array('Page','handleOnClick') // using Page::handleOnClick()
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* To raise an event, use {@link raiseEvent}. The on-method defining an event is
|
||||||
|
* commonly written like the following:
|
||||||
|
* <pre>
|
||||||
|
* public function onClick($event)
|
||||||
|
* {
|
||||||
|
* $this->raiseEvent('onClick',$event);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* where <code>$event</code> is an instance of {@link CEvent} or its child class.
|
||||||
|
* One can then raise the event by calling the on-method instead of {@link raiseEvent} directly.
|
||||||
|
*
|
||||||
|
* Both property names and event names are case-insensitive.
|
||||||
|
*
|
||||||
|
* CComponent supports behaviors. A behavior is an
|
||||||
|
* instance of {@link IBehavior} which is attached to a component. The methods of
|
||||||
|
* the behavior can be invoked as if they belong to the component. Multiple behaviors
|
||||||
|
* can be attached to the same component.
|
||||||
|
*
|
||||||
|
* To attach a behavior to a component, call {@link attachBehavior}; and to detach the behavior
|
||||||
|
* from the component, call {@link detachBehavior}.
|
||||||
|
*
|
||||||
|
* A behavior can be temporarily enabled or disabled by calling {@link enableBehavior}
|
||||||
|
* or {@link disableBehavior}, respectively. When disabled, the behavior methods cannot
|
||||||
|
* be invoked via the component.
|
||||||
|
*
|
||||||
|
* Starting from version 1.1.0, a behavior's properties (either its public member variables or
|
||||||
|
* its properties defined via getters and/or setters) can be accessed through the component it
|
||||||
|
* is attached to.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CComponent
|
||||||
|
{
|
||||||
|
private $_e;
|
||||||
|
private $_m;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a property value, an event handler list or a behavior based on its name.
|
||||||
|
* Do not call this method. This is a PHP magic method that we override
|
||||||
|
* to allow using the following syntax to read a property or obtain event handlers:
|
||||||
|
* <pre>
|
||||||
|
* $value=$component->propertyName;
|
||||||
|
* $handlers=$component->eventName;
|
||||||
|
* </pre>
|
||||||
|
* @param string $name the property name or event name
|
||||||
|
* @return mixed the property value, event handlers attached to the event, or the named behavior
|
||||||
|
* @throws CException if the property or event is not defined
|
||||||
|
* @see __set
|
||||||
|
*/
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
$getter='get'.$name;
|
||||||
|
if(method_exists($this,$getter))
|
||||||
|
return $this->$getter();
|
||||||
|
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
|
||||||
|
{
|
||||||
|
// duplicating getEventHandlers() here for performance
|
||||||
|
$name=strtolower($name);
|
||||||
|
if(!isset($this->_e[$name]))
|
||||||
|
$this->_e[$name]=new CList;
|
||||||
|
return $this->_e[$name];
|
||||||
|
}
|
||||||
|
elseif(isset($this->_m[$name]))
|
||||||
|
return $this->_m[$name];
|
||||||
|
elseif(is_array($this->_m))
|
||||||
|
{
|
||||||
|
foreach($this->_m as $object)
|
||||||
|
{
|
||||||
|
if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
|
||||||
|
return $object->$name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
|
||||||
|
array('{class}'=>get_class($this), '{property}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets value of a component property.
|
||||||
|
* Do not call this method. This is a PHP magic method that we override
|
||||||
|
* to allow using the following syntax to set a property or attach an event handler
|
||||||
|
* <pre>
|
||||||
|
* $this->propertyName=$value;
|
||||||
|
* $this->eventName=$callback;
|
||||||
|
* </pre>
|
||||||
|
* @param string $name the property name or the event name
|
||||||
|
* @param mixed $value the property value or callback
|
||||||
|
* @return mixed
|
||||||
|
* @throws CException if the property/event is not defined or the property is read only.
|
||||||
|
* @see __get
|
||||||
|
*/
|
||||||
|
public function __set($name,$value)
|
||||||
|
{
|
||||||
|
$setter='set'.$name;
|
||||||
|
if(method_exists($this,$setter))
|
||||||
|
return $this->$setter($value);
|
||||||
|
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
|
||||||
|
{
|
||||||
|
// duplicating getEventHandlers() here for performance
|
||||||
|
$name=strtolower($name);
|
||||||
|
if(!isset($this->_e[$name]))
|
||||||
|
$this->_e[$name]=new CList;
|
||||||
|
return $this->_e[$name]->add($value);
|
||||||
|
}
|
||||||
|
elseif(is_array($this->_m))
|
||||||
|
{
|
||||||
|
foreach($this->_m as $object)
|
||||||
|
{
|
||||||
|
if($object->getEnabled() && (property_exists($object,$name) || $object->canSetProperty($name)))
|
||||||
|
return $object->$name=$value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(method_exists($this,'get'.$name))
|
||||||
|
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
|
||||||
|
array('{class}'=>get_class($this), '{property}'=>$name)));
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Property "{class}.{property}" is not defined.',
|
||||||
|
array('{class}'=>get_class($this), '{property}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a property value is null.
|
||||||
|
* Do not call this method. This is a PHP magic method that we override
|
||||||
|
* to allow using isset() to detect if a component property is set or not.
|
||||||
|
* @param string $name the property name or the event name
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
$getter='get'.$name;
|
||||||
|
if(method_exists($this,$getter))
|
||||||
|
return $this->$getter()!==null;
|
||||||
|
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
|
||||||
|
{
|
||||||
|
$name=strtolower($name);
|
||||||
|
return isset($this->_e[$name]) && $this->_e[$name]->getCount();
|
||||||
|
}
|
||||||
|
elseif(is_array($this->_m))
|
||||||
|
{
|
||||||
|
if(isset($this->_m[$name]))
|
||||||
|
return true;
|
||||||
|
foreach($this->_m as $object)
|
||||||
|
{
|
||||||
|
if($object->getEnabled() && (property_exists($object,$name) || $object->canGetProperty($name)))
|
||||||
|
return $object->$name!==null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a component property to be null.
|
||||||
|
* Do not call this method. This is a PHP magic method that we override
|
||||||
|
* to allow using unset() to set a component property to be null.
|
||||||
|
* @param string $name the property name or the event name
|
||||||
|
* @throws CException if the property is read only.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __unset($name)
|
||||||
|
{
|
||||||
|
$setter='set'.$name;
|
||||||
|
if(method_exists($this,$setter))
|
||||||
|
$this->$setter(null);
|
||||||
|
elseif(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
|
||||||
|
unset($this->_e[strtolower($name)]);
|
||||||
|
elseif(is_array($this->_m))
|
||||||
|
{
|
||||||
|
if(isset($this->_m[$name]))
|
||||||
|
$this->detachBehavior($name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($this->_m as $object)
|
||||||
|
{
|
||||||
|
if($object->getEnabled())
|
||||||
|
{
|
||||||
|
if(property_exists($object,$name))
|
||||||
|
return $object->$name=null;
|
||||||
|
elseif($object->canSetProperty($name))
|
||||||
|
return $object->$setter(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif(method_exists($this,'get'.$name))
|
||||||
|
throw new CException(Yii::t('yii','Property "{class}.{property}" is read only.',
|
||||||
|
array('{class}'=>get_class($this), '{property}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the named method which is not a class method.
|
||||||
|
* Do not call this method. This is a PHP magic method that we override
|
||||||
|
* to implement the behavior feature.
|
||||||
|
* @param string $name the method name
|
||||||
|
* @param array $parameters method parameters
|
||||||
|
* @throws CException if current class and its behaviors do not have a method or closure with the given name
|
||||||
|
* @return mixed the method return value
|
||||||
|
*/
|
||||||
|
public function __call($name,$parameters)
|
||||||
|
{
|
||||||
|
if($this->_m!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_m as $object)
|
||||||
|
{
|
||||||
|
if($object->getEnabled() && method_exists($object,$name))
|
||||||
|
return call_user_func_array(array($object,$name),$parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure)
|
||||||
|
return call_user_func_array($this->$name, $parameters);
|
||||||
|
throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".',
|
||||||
|
array('{class}'=>get_class($this), '{name}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the named behavior object.
|
||||||
|
* The name 'asa' stands for 'as a'.
|
||||||
|
* @param string $behavior the behavior name
|
||||||
|
* @return IBehavior the behavior object, or null if the behavior does not exist
|
||||||
|
*/
|
||||||
|
public function asa($behavior)
|
||||||
|
{
|
||||||
|
return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a list of behaviors to the component.
|
||||||
|
* Each behavior is indexed by its name and should be an instance of
|
||||||
|
* {@link IBehavior}, a string specifying the behavior class, or an
|
||||||
|
* array of the following structure:
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'class'=>'path.to.BehaviorClass',
|
||||||
|
* 'property1'=>'value1',
|
||||||
|
* 'property2'=>'value2',
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
* @param array $behaviors list of behaviors to be attached to the component
|
||||||
|
*/
|
||||||
|
public function attachBehaviors($behaviors)
|
||||||
|
{
|
||||||
|
foreach($behaviors as $name=>$behavior)
|
||||||
|
$this->attachBehavior($name,$behavior);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches all behaviors from the component.
|
||||||
|
*/
|
||||||
|
public function detachBehaviors()
|
||||||
|
{
|
||||||
|
if($this->_m!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_m as $name=>$behavior)
|
||||||
|
$this->detachBehavior($name);
|
||||||
|
$this->_m=null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches a behavior to this component.
|
||||||
|
* This method will create the behavior object based on the given
|
||||||
|
* configuration. After that, the behavior object will be initialized
|
||||||
|
* by calling its {@link IBehavior::attach} method.
|
||||||
|
* @param string $name the behavior's name. It should uniquely identify this behavior.
|
||||||
|
* @param mixed $behavior the behavior configuration. This is passed as the first
|
||||||
|
* parameter to {@link YiiBase::createComponent} to create the behavior object.
|
||||||
|
* You can also pass an already created behavior instance (the new behavior will replace an already created
|
||||||
|
* behavior with the same name, if it exists).
|
||||||
|
* @return IBehavior the behavior object
|
||||||
|
*/
|
||||||
|
public function attachBehavior($name,$behavior)
|
||||||
|
{
|
||||||
|
if(!($behavior instanceof IBehavior))
|
||||||
|
$behavior=Yii::createComponent($behavior);
|
||||||
|
$behavior->setEnabled(true);
|
||||||
|
$behavior->attach($this);
|
||||||
|
return $this->_m[$name]=$behavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches a behavior from the component.
|
||||||
|
* The behavior's {@link IBehavior::detach} method will be invoked.
|
||||||
|
* @param string $name the behavior's name. It uniquely identifies the behavior.
|
||||||
|
* @return IBehavior the detached behavior. Null if the behavior does not exist.
|
||||||
|
*/
|
||||||
|
public function detachBehavior($name)
|
||||||
|
{
|
||||||
|
if(isset($this->_m[$name]))
|
||||||
|
{
|
||||||
|
$this->_m[$name]->detach($this);
|
||||||
|
$behavior=$this->_m[$name];
|
||||||
|
unset($this->_m[$name]);
|
||||||
|
return $behavior;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables all behaviors attached to this component.
|
||||||
|
*/
|
||||||
|
public function enableBehaviors()
|
||||||
|
{
|
||||||
|
if($this->_m!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_m as $behavior)
|
||||||
|
$behavior->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables all behaviors attached to this component.
|
||||||
|
*/
|
||||||
|
public function disableBehaviors()
|
||||||
|
{
|
||||||
|
if($this->_m!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_m as $behavior)
|
||||||
|
$behavior->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables an attached behavior.
|
||||||
|
* A behavior is only effective when it is enabled.
|
||||||
|
* A behavior is enabled when first attached.
|
||||||
|
* @param string $name the behavior's name. It uniquely identifies the behavior.
|
||||||
|
*/
|
||||||
|
public function enableBehavior($name)
|
||||||
|
{
|
||||||
|
if(isset($this->_m[$name]))
|
||||||
|
$this->_m[$name]->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disables an attached behavior.
|
||||||
|
* A behavior is only effective when it is enabled.
|
||||||
|
* @param string $name the behavior's name. It uniquely identifies the behavior.
|
||||||
|
*/
|
||||||
|
public function disableBehavior($name)
|
||||||
|
{
|
||||||
|
if(isset($this->_m[$name]))
|
||||||
|
$this->_m[$name]->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a property is defined.
|
||||||
|
* A property is defined if there is a getter or setter method
|
||||||
|
* defined in the class. Note, property names are case-insensitive.
|
||||||
|
* @param string $name the property name
|
||||||
|
* @return boolean whether the property is defined
|
||||||
|
* @see canGetProperty
|
||||||
|
* @see canSetProperty
|
||||||
|
*/
|
||||||
|
public function hasProperty($name)
|
||||||
|
{
|
||||||
|
return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a property can be read.
|
||||||
|
* A property can be read if the class has a getter method
|
||||||
|
* for the property name. Note, property name is case-insensitive.
|
||||||
|
* @param string $name the property name
|
||||||
|
* @return boolean whether the property can be read
|
||||||
|
* @see canSetProperty
|
||||||
|
*/
|
||||||
|
public function canGetProperty($name)
|
||||||
|
{
|
||||||
|
return method_exists($this,'get'.$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a property can be set.
|
||||||
|
* A property can be written if the class has a setter method
|
||||||
|
* for the property name. Note, property name is case-insensitive.
|
||||||
|
* @param string $name the property name
|
||||||
|
* @return boolean whether the property can be written
|
||||||
|
* @see canGetProperty
|
||||||
|
*/
|
||||||
|
public function canSetProperty($name)
|
||||||
|
{
|
||||||
|
return method_exists($this,'set'.$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether an event is defined.
|
||||||
|
* An event is defined if the class has a method named like 'onXXX'.
|
||||||
|
* Note, event name is case-insensitive.
|
||||||
|
* @param string $name the event name
|
||||||
|
* @return boolean whether an event is defined
|
||||||
|
*/
|
||||||
|
public function hasEvent($name)
|
||||||
|
{
|
||||||
|
return !strncasecmp($name,'on',2) && method_exists($this,$name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the named event has attached handlers.
|
||||||
|
* @param string $name the event name
|
||||||
|
* @return boolean whether an event has been attached one or several handlers
|
||||||
|
*/
|
||||||
|
public function hasEventHandler($name)
|
||||||
|
{
|
||||||
|
$name=strtolower($name);
|
||||||
|
return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of attached event handlers for an event.
|
||||||
|
* @param string $name the event name
|
||||||
|
* @return CList list of attached event handlers for the event
|
||||||
|
* @throws CException if the event is not defined
|
||||||
|
*/
|
||||||
|
public function getEventHandlers($name)
|
||||||
|
{
|
||||||
|
if($this->hasEvent($name))
|
||||||
|
{
|
||||||
|
$name=strtolower($name);
|
||||||
|
if(!isset($this->_e[$name]))
|
||||||
|
$this->_e[$name]=new CList;
|
||||||
|
return $this->_e[$name];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
|
||||||
|
array('{class}'=>get_class($this), '{event}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an event handler to an event.
|
||||||
|
*
|
||||||
|
* An event handler must be a valid PHP callback, i.e., a string referring to
|
||||||
|
* a global function name, or an array containing two elements with
|
||||||
|
* the first element being an object and the second element a method name
|
||||||
|
* of the object.
|
||||||
|
*
|
||||||
|
* An event handler must be defined with the following signature,
|
||||||
|
* <pre>
|
||||||
|
* function handlerName($event) {}
|
||||||
|
* </pre>
|
||||||
|
* where $event includes parameters associated with the event.
|
||||||
|
*
|
||||||
|
* This is a convenient method of attaching a handler to an event.
|
||||||
|
* It is equivalent to the following code:
|
||||||
|
* <pre>
|
||||||
|
* $component->getEventHandlers($eventName)->add($eventHandler);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Using {@link getEventHandlers}, one can also specify the excution order
|
||||||
|
* of multiple handlers attaching to the same event. For example:
|
||||||
|
* <pre>
|
||||||
|
* $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
|
||||||
|
* </pre>
|
||||||
|
* makes the handler to be invoked first.
|
||||||
|
*
|
||||||
|
* @param string $name the event name
|
||||||
|
* @param callback $handler the event handler
|
||||||
|
* @throws CException if the event is not defined
|
||||||
|
* @see detachEventHandler
|
||||||
|
*/
|
||||||
|
public function attachEventHandler($name,$handler)
|
||||||
|
{
|
||||||
|
$this->getEventHandlers($name)->add($handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches an existing event handler.
|
||||||
|
* This method is the opposite of {@link attachEventHandler}.
|
||||||
|
* @param string $name event name
|
||||||
|
* @param callback $handler the event handler to be removed
|
||||||
|
* @return boolean if the detachment process is successful
|
||||||
|
* @see attachEventHandler
|
||||||
|
*/
|
||||||
|
public function detachEventHandler($name,$handler)
|
||||||
|
{
|
||||||
|
if($this->hasEventHandler($name))
|
||||||
|
return $this->getEventHandlers($name)->remove($handler)!==false;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raises an event.
|
||||||
|
* This method represents the happening of an event. It invokes
|
||||||
|
* all attached handlers for the event.
|
||||||
|
* @param string $name the event name
|
||||||
|
* @param CEvent $event the event parameter
|
||||||
|
* @throws CException if the event is undefined or an event handler is invalid.
|
||||||
|
*/
|
||||||
|
public function raiseEvent($name,$event)
|
||||||
|
{
|
||||||
|
$name=strtolower($name);
|
||||||
|
if(isset($this->_e[$name]))
|
||||||
|
{
|
||||||
|
foreach($this->_e[$name] as $handler)
|
||||||
|
{
|
||||||
|
if(is_string($handler))
|
||||||
|
call_user_func($handler,$event);
|
||||||
|
elseif(is_callable($handler,true))
|
||||||
|
{
|
||||||
|
if(is_array($handler))
|
||||||
|
{
|
||||||
|
// an array: 0 - object, 1 - method name
|
||||||
|
list($object,$method)=$handler;
|
||||||
|
if(is_string($object)) // static method call
|
||||||
|
call_user_func($handler,$event);
|
||||||
|
elseif(method_exists($object,$method))
|
||||||
|
$object->$method($event);
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
|
||||||
|
array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));
|
||||||
|
}
|
||||||
|
else // PHP 5.3: anonymous function
|
||||||
|
call_user_func($handler,$event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','Event "{class}.{event}" is attached with an invalid handler "{handler}".',
|
||||||
|
array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));
|
||||||
|
// stop further handling if param.handled is set true
|
||||||
|
if(($event instanceof CEvent) && $event->handled)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif(YII_DEBUG && !$this->hasEvent($name))
|
||||||
|
throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.',
|
||||||
|
array('{class}'=>get_class($this), '{event}'=>$name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates a PHP expression or callback under the context of this component.
|
||||||
|
*
|
||||||
|
* Valid PHP callback can be class method name in the form of
|
||||||
|
* array(ClassName/Object, MethodName), or anonymous function (only available in PHP 5.3.0 or above).
|
||||||
|
*
|
||||||
|
* If a PHP callback is used, the corresponding function/method signature should be
|
||||||
|
* <pre>
|
||||||
|
* function foo($param1, $param2, ..., $component) { ... }
|
||||||
|
* </pre>
|
||||||
|
* where the array elements in the second parameter to this method will be passed
|
||||||
|
* to the callback as $param1, $param2, ...; and the last parameter will be the component itself.
|
||||||
|
*
|
||||||
|
* If a PHP expression is used, the second parameter will be "extracted" into PHP variables
|
||||||
|
* that can be directly accessed in the expression. See {@link http://us.php.net/manual/en/function.extract.php PHP extract}
|
||||||
|
* for more details. In the expression, the component object can be accessed using $this.
|
||||||
|
*
|
||||||
|
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
|
||||||
|
* please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
|
||||||
|
*
|
||||||
|
* @param mixed $_expression_ a PHP expression or PHP callback to be evaluated.
|
||||||
|
* @param array $_data_ additional parameters to be passed to the above expression/callback.
|
||||||
|
* @return mixed the expression result
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
public function evaluateExpression($_expression_,$_data_=array())
|
||||||
|
{
|
||||||
|
if(is_string($_expression_))
|
||||||
|
{
|
||||||
|
extract($_data_);
|
||||||
|
return eval('return '.$_expression_.';');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$_data_[]=$this;
|
||||||
|
return call_user_func_array($_expression_, $_data_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CEvent is the base class for all event classes.
|
||||||
|
*
|
||||||
|
* It encapsulates the parameters associated with an event.
|
||||||
|
* The {@link sender} property describes who raises the event.
|
||||||
|
* And the {@link handled} property indicates if the event is handled.
|
||||||
|
* If an event handler sets {@link handled} to true, those handlers
|
||||||
|
* that are not invoked yet will not be invoked anymore.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CEvent extends CComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var object the sender of this event
|
||||||
|
*/
|
||||||
|
public $sender;
|
||||||
|
/**
|
||||||
|
* @var boolean whether the event is handled. Defaults to false.
|
||||||
|
* When a handler sets this true, the rest of the uninvoked event handlers will not be invoked anymore.
|
||||||
|
*/
|
||||||
|
public $handled=false;
|
||||||
|
/**
|
||||||
|
* @var mixed additional event parameters.
|
||||||
|
* @since 1.1.7
|
||||||
|
*/
|
||||||
|
public $params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param mixed $sender sender of the event
|
||||||
|
* @param mixed $params additional parameters for the event
|
||||||
|
*/
|
||||||
|
public function __construct($sender=null,$params=null)
|
||||||
|
{
|
||||||
|
$this->sender=$sender;
|
||||||
|
$this->params=$params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CEnumerable is the base class for all enumerable types.
|
||||||
|
*
|
||||||
|
* To define an enumerable type, extend CEnumberable and define string constants.
|
||||||
|
* Each constant represents an enumerable value.
|
||||||
|
* The constant name must be the same as the constant value.
|
||||||
|
* For example,
|
||||||
|
* <pre>
|
||||||
|
* class TextAlign extends CEnumerable
|
||||||
|
* {
|
||||||
|
* const Left='Left';
|
||||||
|
* const Right='Right';
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* Then, one can use the enumerable values such as TextAlign::Left and
|
||||||
|
* TextAlign::Right.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CEnumerable
|
||||||
|
{
|
||||||
|
}
|
53
framework/base/CErrorEvent.php
Normal file
53
framework/base/CErrorEvent.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CErrorEvent class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CErrorEvent represents the parameter for the {@link CApplication::onError onError} event.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CErrorEvent extends CEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string error code
|
||||||
|
*/
|
||||||
|
public $code;
|
||||||
|
/**
|
||||||
|
* @var string error message
|
||||||
|
*/
|
||||||
|
public $message;
|
||||||
|
/**
|
||||||
|
* @var string error message
|
||||||
|
*/
|
||||||
|
public $file;
|
||||||
|
/**
|
||||||
|
* @var string error file
|
||||||
|
*/
|
||||||
|
public $line;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param mixed $sender sender of the event
|
||||||
|
* @param string $code error code
|
||||||
|
* @param string $message error message
|
||||||
|
* @param string $file error file
|
||||||
|
* @param integer $line error line
|
||||||
|
*/
|
||||||
|
public function __construct($sender,$code,$message,$file,$line)
|
||||||
|
{
|
||||||
|
$this->code=$code;
|
||||||
|
$this->message=$message;
|
||||||
|
$this->file=$file;
|
||||||
|
$this->line=$line;
|
||||||
|
parent::__construct($sender);
|
||||||
|
}
|
||||||
|
}
|
578
framework/base/CErrorHandler.php
Normal file
578
framework/base/CErrorHandler.php
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains the error handler application component.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
Yii::import('CHtml',true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CErrorHandler handles uncaught PHP errors and exceptions.
|
||||||
|
*
|
||||||
|
* It displays these errors using appropriate views based on the
|
||||||
|
* nature of the error and the mode the application runs at.
|
||||||
|
* It also chooses the most preferred language for displaying the error.
|
||||||
|
*
|
||||||
|
* CErrorHandler uses two sets of views:
|
||||||
|
* <ul>
|
||||||
|
* <li>development views, named as <code>exception.php</code>;
|
||||||
|
* <li>production views, named as <code>error<StatusCode>.php</code>;
|
||||||
|
* </ul>
|
||||||
|
* where <StatusCode> stands for the HTTP error code (e.g. error500.php).
|
||||||
|
* Localized views are named similarly but located under a subdirectory
|
||||||
|
* whose name is the language code (e.g. zh_cn/error500.php).
|
||||||
|
*
|
||||||
|
* Development views are displayed when the application is in debug mode
|
||||||
|
* (i.e. YII_DEBUG is defined as true). Detailed error information with source code
|
||||||
|
* are displayed in these views. Production views are meant to be shown
|
||||||
|
* to end-users and are used when the application is in production mode.
|
||||||
|
* For security reasons, they only display the error message without any
|
||||||
|
* sensitive information.
|
||||||
|
*
|
||||||
|
* CErrorHandler looks for the view templates from the following locations in order:
|
||||||
|
* <ol>
|
||||||
|
* <li><code>themes/ThemeName/views/system</code>: when a theme is active.</li>
|
||||||
|
* <li><code>protected/views/system</code></li>
|
||||||
|
* <li><code>framework/views</code></li>
|
||||||
|
* </ol>
|
||||||
|
* If the view is not found in a directory, it will be looked for in the next directory.
|
||||||
|
*
|
||||||
|
* The property {@link maxSourceLines} can be changed to specify the number
|
||||||
|
* of source code lines to be displayed in development views.
|
||||||
|
*
|
||||||
|
* CErrorHandler is a core application component that can be accessed via
|
||||||
|
* {@link CApplication::getErrorHandler()}.
|
||||||
|
*
|
||||||
|
* @property array $error The error details. Null if there is no error.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CErrorHandler extends CApplicationComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var integer maximum number of source code lines to be displayed. Defaults to 25.
|
||||||
|
*/
|
||||||
|
public $maxSourceLines=25;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer maximum number of trace source code lines to be displayed. Defaults to 10.
|
||||||
|
* @since 1.1.6
|
||||||
|
*/
|
||||||
|
public $maxTraceSourceLines = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string the application administrator information (could be a name or email link). It is displayed in error pages to end users. Defaults to 'the webmaster'.
|
||||||
|
*/
|
||||||
|
public $adminInfo='the webmaster';
|
||||||
|
/**
|
||||||
|
* @var boolean whether to discard any existing page output before error display. Defaults to true.
|
||||||
|
*/
|
||||||
|
public $discardOutput=true;
|
||||||
|
/**
|
||||||
|
* @var string the route (eg 'site/error') to the controller action that will be used to display external errors.
|
||||||
|
* Inside the action, it can retrieve the error information by Yii::app()->errorHandler->error.
|
||||||
|
* This property defaults to null, meaning CErrorHandler will handle the error display.
|
||||||
|
*/
|
||||||
|
public $errorAction;
|
||||||
|
|
||||||
|
private $_error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the exception/error event.
|
||||||
|
* This method is invoked by the application whenever it captures
|
||||||
|
* an exception or PHP error.
|
||||||
|
* @param CEvent $event the event containing the exception/error information
|
||||||
|
*/
|
||||||
|
public function handle($event)
|
||||||
|
{
|
||||||
|
// set event as handled to prevent it from being handled by other event handlers
|
||||||
|
$event->handled=true;
|
||||||
|
|
||||||
|
if($this->discardOutput)
|
||||||
|
{
|
||||||
|
$gzHandler=false;
|
||||||
|
foreach(ob_list_handlers() as $h)
|
||||||
|
{
|
||||||
|
if(strpos($h,'gzhandler')!==false)
|
||||||
|
$gzHandler=true;
|
||||||
|
}
|
||||||
|
// the following manual level counting is to deal with zlib.output_compression set to On
|
||||||
|
// for an output buffer created by zlib.output_compression set to On ob_end_clean will fail
|
||||||
|
for($level=ob_get_level();$level>0;--$level)
|
||||||
|
{
|
||||||
|
if(!@ob_end_clean())
|
||||||
|
ob_clean();
|
||||||
|
}
|
||||||
|
// reset headers in case there was an ob_start("ob_gzhandler") before
|
||||||
|
if($gzHandler && !headers_sent() && ob_list_handlers()===array())
|
||||||
|
{
|
||||||
|
if(function_exists('header_remove')) // php >= 5.3
|
||||||
|
{
|
||||||
|
header_remove('Vary');
|
||||||
|
header_remove('Content-Encoding');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
header('Vary:');
|
||||||
|
header('Content-Encoding:');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($event instanceof CExceptionEvent)
|
||||||
|
$this->handleException($event->exception);
|
||||||
|
else // CErrorEvent
|
||||||
|
$this->handleError($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the details about the error that is currently being handled.
|
||||||
|
* The error is returned in terms of an array, with the following information:
|
||||||
|
* <ul>
|
||||||
|
* <li>code - the HTTP status code (e.g. 403, 500)</li>
|
||||||
|
* <li>type - the error type (e.g. 'CHttpException', 'PHP Error')</li>
|
||||||
|
* <li>message - the error message</li>
|
||||||
|
* <li>file - the name of the PHP script file where the error occurs</li>
|
||||||
|
* <li>line - the line number of the code where the error occurs</li>
|
||||||
|
* <li>trace - the call stack of the error</li>
|
||||||
|
* <li>source - the context source code where the error occurs</li>
|
||||||
|
* </ul>
|
||||||
|
* @return array the error details. Null if there is no error.
|
||||||
|
*/
|
||||||
|
public function getError()
|
||||||
|
{
|
||||||
|
return $this->_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the exception.
|
||||||
|
* @param Exception $exception the exception captured
|
||||||
|
*/
|
||||||
|
protected function handleException($exception)
|
||||||
|
{
|
||||||
|
$app=Yii::app();
|
||||||
|
if($app instanceof CWebApplication)
|
||||||
|
{
|
||||||
|
if(($trace=$this->getExactTrace($exception))===null)
|
||||||
|
{
|
||||||
|
$fileName=$exception->getFile();
|
||||||
|
$errorLine=$exception->getLine();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$fileName=$trace['file'];
|
||||||
|
$errorLine=$trace['line'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$trace = $exception->getTrace();
|
||||||
|
|
||||||
|
foreach($trace as $i=>$t)
|
||||||
|
{
|
||||||
|
if(!isset($t['file']))
|
||||||
|
$trace[$i]['file']='unknown';
|
||||||
|
|
||||||
|
if(!isset($t['line']))
|
||||||
|
$trace[$i]['line']=0;
|
||||||
|
|
||||||
|
if(!isset($t['function']))
|
||||||
|
$trace[$i]['function']='unknown';
|
||||||
|
|
||||||
|
unset($trace[$i]['object']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_error=$data=array(
|
||||||
|
'code'=>($exception instanceof CHttpException)?$exception->statusCode:500,
|
||||||
|
'type'=>get_class($exception),
|
||||||
|
'errorCode'=>$exception->getCode(),
|
||||||
|
'message'=>$exception->getMessage(),
|
||||||
|
'file'=>$fileName,
|
||||||
|
'line'=>$errorLine,
|
||||||
|
'trace'=>$exception->getTraceAsString(),
|
||||||
|
'traces'=>$trace,
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!headers_sent())
|
||||||
|
header("HTTP/1.0 {$data['code']} ".$this->getHttpHeader($data['code'], get_class($exception)));
|
||||||
|
|
||||||
|
if($exception instanceof CHttpException || !YII_DEBUG)
|
||||||
|
$this->render('error',$data);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if($this->isAjaxRequest())
|
||||||
|
$app->displayException($exception);
|
||||||
|
else
|
||||||
|
$this->render('exception',$data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$app->displayException($exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the PHP error.
|
||||||
|
* @param CErrorEvent $event the PHP error event
|
||||||
|
*/
|
||||||
|
protected function handleError($event)
|
||||||
|
{
|
||||||
|
$trace=debug_backtrace();
|
||||||
|
// skip the first 3 stacks as they do not tell the error position
|
||||||
|
if(count($trace)>3)
|
||||||
|
$trace=array_slice($trace,3);
|
||||||
|
$traceString='';
|
||||||
|
foreach($trace as $i=>$t)
|
||||||
|
{
|
||||||
|
if(!isset($t['file']))
|
||||||
|
$trace[$i]['file']='unknown';
|
||||||
|
|
||||||
|
if(!isset($t['line']))
|
||||||
|
$trace[$i]['line']=0;
|
||||||
|
|
||||||
|
if(!isset($t['function']))
|
||||||
|
$trace[$i]['function']='unknown';
|
||||||
|
|
||||||
|
$traceString.="#$i {$trace[$i]['file']}({$trace[$i]['line']}): ";
|
||||||
|
if(isset($t['object']) && is_object($t['object']))
|
||||||
|
$traceString.=get_class($t['object']).'->';
|
||||||
|
$traceString.="{$trace[$i]['function']}()\n";
|
||||||
|
|
||||||
|
unset($trace[$i]['object']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$app=Yii::app();
|
||||||
|
if($app instanceof CWebApplication)
|
||||||
|
{
|
||||||
|
switch($event->code)
|
||||||
|
{
|
||||||
|
case E_WARNING:
|
||||||
|
$type = 'PHP warning';
|
||||||
|
break;
|
||||||
|
case E_NOTICE:
|
||||||
|
$type = 'PHP notice';
|
||||||
|
break;
|
||||||
|
case E_USER_ERROR:
|
||||||
|
$type = 'User error';
|
||||||
|
break;
|
||||||
|
case E_USER_WARNING:
|
||||||
|
$type = 'User warning';
|
||||||
|
break;
|
||||||
|
case E_USER_NOTICE:
|
||||||
|
$type = 'User notice';
|
||||||
|
break;
|
||||||
|
case E_RECOVERABLE_ERROR:
|
||||||
|
$type = 'Recoverable error';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$type = 'PHP error';
|
||||||
|
}
|
||||||
|
$this->_error=$data=array(
|
||||||
|
'code'=>500,
|
||||||
|
'type'=>$type,
|
||||||
|
'message'=>$event->message,
|
||||||
|
'file'=>$event->file,
|
||||||
|
'line'=>$event->line,
|
||||||
|
'trace'=>$traceString,
|
||||||
|
'traces'=>$trace,
|
||||||
|
);
|
||||||
|
if(!headers_sent())
|
||||||
|
header("HTTP/1.0 500 Internal Server Error");
|
||||||
|
if($this->isAjaxRequest())
|
||||||
|
$app->displayError($event->code,$event->message,$event->file,$event->line);
|
||||||
|
elseif(YII_DEBUG)
|
||||||
|
$this->render('exception',$data);
|
||||||
|
else
|
||||||
|
$this->render('error',$data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$app->displayError($event->code,$event->message,$event->file,$event->line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* whether the current request is an AJAX (XMLHttpRequest) request.
|
||||||
|
* @return boolean whether the current request is an AJAX request.
|
||||||
|
*/
|
||||||
|
protected function isAjaxRequest()
|
||||||
|
{
|
||||||
|
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exact trace where the problem occurs.
|
||||||
|
* @param Exception $exception the uncaught exception
|
||||||
|
* @return array the exact trace where the problem occurs
|
||||||
|
*/
|
||||||
|
protected function getExactTrace($exception)
|
||||||
|
{
|
||||||
|
$traces=$exception->getTrace();
|
||||||
|
|
||||||
|
foreach($traces as $trace)
|
||||||
|
{
|
||||||
|
// property access exception
|
||||||
|
if(isset($trace['function']) && ($trace['function']==='__get' || $trace['function']==='__set'))
|
||||||
|
return $trace;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the view.
|
||||||
|
* @param string $view the view name (file name without extension).
|
||||||
|
* See {@link getViewFile} for how a view file is located given its name.
|
||||||
|
* @param array $data data to be passed to the view
|
||||||
|
*/
|
||||||
|
protected function render($view,$data)
|
||||||
|
{
|
||||||
|
if($view==='error' && $this->errorAction!==null)
|
||||||
|
Yii::app()->runController($this->errorAction);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// additional information to be passed to view
|
||||||
|
$data['version']=$this->getVersionInfo();
|
||||||
|
$data['time']=time();
|
||||||
|
$data['admin']=$this->adminInfo;
|
||||||
|
include($this->getViewFile($view,$data['code']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines which view file should be used.
|
||||||
|
* @param string $view view name (either 'exception' or 'error')
|
||||||
|
* @param integer $code HTTP status code
|
||||||
|
* @return string view file path
|
||||||
|
*/
|
||||||
|
protected function getViewFile($view,$code)
|
||||||
|
{
|
||||||
|
$viewPaths=array(
|
||||||
|
Yii::app()->getTheme()===null ? null : Yii::app()->getTheme()->getSystemViewPath(),
|
||||||
|
Yii::app() instanceof CWebApplication ? Yii::app()->getSystemViewPath() : null,
|
||||||
|
YII_PATH.DIRECTORY_SEPARATOR.'views',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach($viewPaths as $i=>$viewPath)
|
||||||
|
{
|
||||||
|
if($viewPath!==null)
|
||||||
|
{
|
||||||
|
$viewFile=$this->getViewFileInternal($viewPath,$view,$code,$i===2?'en_us':null);
|
||||||
|
if(is_file($viewFile))
|
||||||
|
return $viewFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks for the view under the specified directory.
|
||||||
|
* @param string $viewPath the directory containing the views
|
||||||
|
* @param string $view view name (either 'exception' or 'error')
|
||||||
|
* @param integer $code HTTP status code
|
||||||
|
* @param string $srcLanguage the language that the view file is in
|
||||||
|
* @return string view file path
|
||||||
|
*/
|
||||||
|
protected function getViewFileInternal($viewPath,$view,$code,$srcLanguage=null)
|
||||||
|
{
|
||||||
|
$app=Yii::app();
|
||||||
|
if($view==='error')
|
||||||
|
{
|
||||||
|
$viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR."error{$code}.php",$srcLanguage);
|
||||||
|
if(!is_file($viewFile))
|
||||||
|
$viewFile=$app->findLocalizedFile($viewPath.DIRECTORY_SEPARATOR.'error.php',$srcLanguage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$viewFile=$viewPath.DIRECTORY_SEPARATOR."exception.php";
|
||||||
|
return $viewFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns server version information.
|
||||||
|
* If the application is in production mode, empty string is returned.
|
||||||
|
* @return string server version information. Empty if in production mode.
|
||||||
|
*/
|
||||||
|
protected function getVersionInfo()
|
||||||
|
{
|
||||||
|
if(YII_DEBUG)
|
||||||
|
{
|
||||||
|
$version='<a href="http://www.yiiframework.com/">Yii Framework</a>/'.Yii::getVersion();
|
||||||
|
if(isset($_SERVER['SERVER_SOFTWARE']))
|
||||||
|
$version=$_SERVER['SERVER_SOFTWARE'].' '.$version;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$version='';
|
||||||
|
return $version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts arguments array to its string representation
|
||||||
|
*
|
||||||
|
* @param array $args arguments array to be converted
|
||||||
|
* @return string string representation of the arguments array
|
||||||
|
*/
|
||||||
|
protected function argumentsToString($args)
|
||||||
|
{
|
||||||
|
$count=0;
|
||||||
|
|
||||||
|
$isAssoc=$args!==array_values($args);
|
||||||
|
|
||||||
|
foreach($args as $key => $value)
|
||||||
|
{
|
||||||
|
$count++;
|
||||||
|
if($count>=5)
|
||||||
|
{
|
||||||
|
if($count>5)
|
||||||
|
unset($args[$key]);
|
||||||
|
else
|
||||||
|
$args[$key]='...';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_object($value))
|
||||||
|
$args[$key] = get_class($value);
|
||||||
|
elseif(is_bool($value))
|
||||||
|
$args[$key] = $value ? 'true' : 'false';
|
||||||
|
elseif(is_string($value))
|
||||||
|
{
|
||||||
|
if(strlen($value)>64)
|
||||||
|
$args[$key] = '"'.substr($value,0,64).'..."';
|
||||||
|
else
|
||||||
|
$args[$key] = '"'.$value.'"';
|
||||||
|
}
|
||||||
|
elseif(is_array($value))
|
||||||
|
$args[$key] = 'array('.$this->argumentsToString($value).')';
|
||||||
|
elseif($value===null)
|
||||||
|
$args[$key] = 'null';
|
||||||
|
elseif(is_resource($value))
|
||||||
|
$args[$key] = 'resource';
|
||||||
|
|
||||||
|
if(is_string($key))
|
||||||
|
{
|
||||||
|
$args[$key] = '"'.$key.'" => '.$args[$key];
|
||||||
|
}
|
||||||
|
elseif($isAssoc)
|
||||||
|
{
|
||||||
|
$args[$key] = $key.' => '.$args[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$out = implode(", ", $args);
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the call stack is from application code.
|
||||||
|
* @param array $trace the trace data
|
||||||
|
* @return boolean whether the call stack is from application code.
|
||||||
|
*/
|
||||||
|
protected function isCoreCode($trace)
|
||||||
|
{
|
||||||
|
if(isset($trace['file']))
|
||||||
|
{
|
||||||
|
$systemPath=realpath(dirname(__FILE__).'/..');
|
||||||
|
return $trace['file']==='unknown' || strpos(realpath($trace['file']),$systemPath.DIRECTORY_SEPARATOR)===0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the source code around the error line.
|
||||||
|
* @param string $file source file path
|
||||||
|
* @param integer $errorLine the error line number
|
||||||
|
* @param integer $maxLines maximum number of lines to display
|
||||||
|
* @return string the rendering result
|
||||||
|
*/
|
||||||
|
protected function renderSourceCode($file,$errorLine,$maxLines)
|
||||||
|
{
|
||||||
|
$errorLine--; // adjust line number to 0-based from 1-based
|
||||||
|
if($errorLine<0 || ($lines=@file($file))===false || ($lineCount=count($lines))<=$errorLine)
|
||||||
|
return '';
|
||||||
|
|
||||||
|
$halfLines=(int)($maxLines/2);
|
||||||
|
$beginLine=$errorLine-$halfLines>0 ? $errorLine-$halfLines:0;
|
||||||
|
$endLine=$errorLine+$halfLines<$lineCount?$errorLine+$halfLines:$lineCount-1;
|
||||||
|
$lineNumberWidth=strlen($endLine+1);
|
||||||
|
|
||||||
|
$output='';
|
||||||
|
for($i=$beginLine;$i<=$endLine;++$i)
|
||||||
|
{
|
||||||
|
$isErrorLine = $i===$errorLine;
|
||||||
|
$code=sprintf("<span class=\"ln".($isErrorLine?' error-ln':'')."\">%0{$lineNumberWidth}d</span> %s",$i+1,CHtml::encode(str_replace("\t",' ',$lines[$i])));
|
||||||
|
if(!$isErrorLine)
|
||||||
|
$output.=$code;
|
||||||
|
else
|
||||||
|
$output.='<span class="error">'.$code.'</span>';
|
||||||
|
}
|
||||||
|
return '<div class="code"><pre>'.$output.'</pre></div>';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return correct message for each known http error code
|
||||||
|
* @param integer $httpCode error code to map
|
||||||
|
* @param string $replacement replacement error string that is returned if code is unknown
|
||||||
|
* @return string the textual representation of the given error code or the replacement string if the error code is unknown
|
||||||
|
*/
|
||||||
|
protected function getHttpHeader($httpCode, $replacement='')
|
||||||
|
{
|
||||||
|
$httpCodes = array(
|
||||||
|
100 => 'Continue',
|
||||||
|
101 => 'Switching Protocols',
|
||||||
|
102 => 'Processing',
|
||||||
|
118 => 'Connection timed out',
|
||||||
|
200 => 'OK',
|
||||||
|
201 => 'Created',
|
||||||
|
202 => 'Accepted',
|
||||||
|
203 => 'Non-Authoritative',
|
||||||
|
204 => 'No Content',
|
||||||
|
205 => 'Reset Content',
|
||||||
|
206 => 'Partial Content',
|
||||||
|
207 => 'Multi-Status',
|
||||||
|
210 => 'Content Different',
|
||||||
|
300 => 'Multiple Choices',
|
||||||
|
301 => 'Moved Permanently',
|
||||||
|
302 => 'Found',
|
||||||
|
303 => 'See Other',
|
||||||
|
304 => 'Not Modified',
|
||||||
|
305 => 'Use Proxy',
|
||||||
|
307 => 'Temporary Redirect',
|
||||||
|
310 => 'Too many Redirect',
|
||||||
|
400 => 'Bad Request',
|
||||||
|
401 => 'Unauthorized',
|
||||||
|
402 => 'Payment Required',
|
||||||
|
403 => 'Forbidden',
|
||||||
|
404 => 'Not Found',
|
||||||
|
405 => 'Method Not Allowed',
|
||||||
|
406 => 'Not Acceptable',
|
||||||
|
407 => 'Proxy Authentication Required',
|
||||||
|
408 => 'Request Time-out',
|
||||||
|
409 => 'Conflict',
|
||||||
|
410 => 'Gone',
|
||||||
|
411 => 'Length Required',
|
||||||
|
412 => 'Precondition Failed',
|
||||||
|
413 => 'Request Entity Too Large',
|
||||||
|
414 => 'Request-URI Too Long',
|
||||||
|
415 => 'Unsupported Media Type',
|
||||||
|
416 => 'Requested range unsatisfiable',
|
||||||
|
417 => 'Expectation failed',
|
||||||
|
418 => 'I’m a teapot',
|
||||||
|
422 => 'Unprocessable entity',
|
||||||
|
423 => 'Locked',
|
||||||
|
424 => 'Method failure',
|
||||||
|
425 => 'Unordered Collection',
|
||||||
|
426 => 'Upgrade Required',
|
||||||
|
449 => 'Retry With',
|
||||||
|
450 => 'Blocked by Windows Parental Controls',
|
||||||
|
500 => 'Internal Server Error',
|
||||||
|
501 => 'Not Implemented',
|
||||||
|
502 => 'Bad Gateway ou Proxy Error',
|
||||||
|
503 => 'Service Unavailable',
|
||||||
|
504 => 'Gateway Time-out',
|
||||||
|
505 => 'HTTP Version not supported',
|
||||||
|
507 => 'Insufficient storage',
|
||||||
|
509 => 'Bandwidth Limit Exceeded',
|
||||||
|
);
|
||||||
|
if(isset($httpCodes[$httpCode]))
|
||||||
|
return $httpCodes[$httpCode];
|
||||||
|
else
|
||||||
|
return $replacement;
|
||||||
|
}
|
||||||
|
}
|
21
framework/base/CException.php
Normal file
21
framework/base/CException.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CException class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CException represents a generic exception for all purposes.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CException extends Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
35
framework/base/CExceptionEvent.php
Normal file
35
framework/base/CExceptionEvent.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CExceptionEvent class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CExceptionEvent represents the parameter for the {@link CApplication::onException onException} event.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CExceptionEvent extends CEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var CException the exception that this event is about.
|
||||||
|
*/
|
||||||
|
public $exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param mixed $sender sender of the event
|
||||||
|
* @param CException $exception the exception
|
||||||
|
*/
|
||||||
|
public function __construct($sender,$exception)
|
||||||
|
{
|
||||||
|
$this->exception=$exception;
|
||||||
|
parent::__construct($sender);
|
||||||
|
}
|
||||||
|
}
|
39
framework/base/CHttpException.php
Normal file
39
framework/base/CHttpException.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CHttpException class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CHttpException represents an exception caused by invalid operations of end-users.
|
||||||
|
*
|
||||||
|
* The HTTP error code can be obtained via {@link statusCode}.
|
||||||
|
* Error handlers may use this status code to decide how to format the error page.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CHttpException extends CException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var integer HTTP status code, such as 403, 404, 500, etc.
|
||||||
|
*/
|
||||||
|
public $statusCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param integer $status HTTP status code, such as 404, 500, etc.
|
||||||
|
* @param string $message error message
|
||||||
|
* @param integer $code error code
|
||||||
|
*/
|
||||||
|
public function __construct($status,$message=null,$code=0)
|
||||||
|
{
|
||||||
|
$this->statusCode=$status;
|
||||||
|
parent::__construct($message,$code);
|
||||||
|
}
|
||||||
|
}
|
619
framework/base/CModel.php
Normal file
619
framework/base/CModel.php
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CModel class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CModel is the base class providing the common features needed by data model objects.
|
||||||
|
*
|
||||||
|
* CModel defines the basic framework for data models that need to be validated.
|
||||||
|
*
|
||||||
|
* @property CList $validatorList All the validators declared in the model.
|
||||||
|
* @property array $validators The validators applicable to the current {@link scenario}.
|
||||||
|
* @property array $errors Errors for all attributes or the specified attribute. Empty array is returned if no error.
|
||||||
|
* @property array $attributes Attribute values (name=>value).
|
||||||
|
* @property string $scenario The scenario that this model is in.
|
||||||
|
* @property array $safeAttributeNames Safe attribute names.
|
||||||
|
* @property CMapIterator $iterator An iterator for traversing the items in the list.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
abstract class CModel extends CComponent implements IteratorAggregate, ArrayAccess
|
||||||
|
{
|
||||||
|
private $_errors=array(); // attribute name => array of errors
|
||||||
|
private $_validators; // validators
|
||||||
|
private $_scenario=''; // scenario
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of attribute names of the model.
|
||||||
|
* @return array list of attribute names.
|
||||||
|
*/
|
||||||
|
abstract public function attributeNames();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the validation rules for attributes.
|
||||||
|
*
|
||||||
|
* This method should be overridden to declare validation rules.
|
||||||
|
* Each rule is an array with the following structure:
|
||||||
|
* <pre>
|
||||||
|
* array('attribute list', 'validator name', 'on'=>'scenario name', ...validation parameters...)
|
||||||
|
* </pre>
|
||||||
|
* where
|
||||||
|
* <ul>
|
||||||
|
* <li>attribute list: specifies the attributes (separated by commas) to be validated;</li>
|
||||||
|
* <li>validator name: specifies the validator to be used. It can be the name of a model class
|
||||||
|
* method, the name of a built-in validator, or a validator class (or its path alias).
|
||||||
|
* A validation method must have the following signature:
|
||||||
|
* <pre>
|
||||||
|
* // $params refers to validation parameters given in the rule
|
||||||
|
* function validatorName($attribute,$params)
|
||||||
|
* </pre>
|
||||||
|
* A built-in validator refers to one of the validators declared in {@link CValidator::builtInValidators}.
|
||||||
|
* And a validator class is a class extending {@link CValidator}.</li>
|
||||||
|
* <li>on: this specifies the scenarios when the validation rule should be performed.
|
||||||
|
* Separate different scenarios with commas. If this option is not set, the rule
|
||||||
|
* will be applied in any scenario that is not listed in "except". Please see {@link scenario} for more details about this option.</li>
|
||||||
|
* <li>except: this specifies the scenarios when the validation rule should not be performed.
|
||||||
|
* Separate different scenarios with commas. Please see {@link scenario} for more details about this option.</li>
|
||||||
|
* <li>additional parameters are used to initialize the corresponding validator properties.
|
||||||
|
* Please refer to individal validator class API for possible properties.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* The following are some examples:
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* array('username', 'required'),
|
||||||
|
* array('username', 'length', 'min'=>3, 'max'=>12),
|
||||||
|
* array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
|
||||||
|
* array('password', 'authenticate', 'on'=>'login'),
|
||||||
|
* );
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Note, in order to inherit rules defined in the parent class, a child class needs to
|
||||||
|
* merge the parent rules with child rules using functions like array_merge().
|
||||||
|
*
|
||||||
|
* @return array validation rules to be applied when {@link validate()} is called.
|
||||||
|
* @see scenario
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of behaviors that this model should behave as.
|
||||||
|
* The return value should be an array of behavior configurations indexed by
|
||||||
|
* behavior names. Each behavior configuration can be either a string specifying
|
||||||
|
* the behavior class or an array of the following structure:
|
||||||
|
* <pre>
|
||||||
|
* 'behaviorName'=>array(
|
||||||
|
* 'class'=>'path.to.BehaviorClass',
|
||||||
|
* 'property1'=>'value1',
|
||||||
|
* 'property2'=>'value2',
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Note, the behavior classes must implement {@link IBehavior} or extend from
|
||||||
|
* {@link CBehavior}. Behaviors declared in this method will be attached
|
||||||
|
* to the model when it is instantiated.
|
||||||
|
*
|
||||||
|
* For more details about behaviors, see {@link CComponent}.
|
||||||
|
* @return array the behavior configurations (behavior name=>behavior configuration)
|
||||||
|
*/
|
||||||
|
public function behaviors()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attribute labels.
|
||||||
|
* Attribute labels are mainly used in error messages of validation.
|
||||||
|
* By default an attribute label is generated using {@link generateAttributeLabel}.
|
||||||
|
* This method allows you to explicitly specify attribute labels.
|
||||||
|
*
|
||||||
|
* Note, in order to inherit labels defined in the parent class, a child class needs to
|
||||||
|
* merge the parent labels with child labels using functions like array_merge().
|
||||||
|
*
|
||||||
|
* @return array attribute labels (name=>label)
|
||||||
|
* @see generateAttributeLabel
|
||||||
|
*/
|
||||||
|
public function attributeLabels()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the validation.
|
||||||
|
*
|
||||||
|
* This method executes the validation rules as declared in {@link rules}.
|
||||||
|
* Only the rules applicable to the current {@link scenario} will be executed.
|
||||||
|
* A rule is considered applicable to a scenario if its 'on' option is not set
|
||||||
|
* or contains the scenario.
|
||||||
|
*
|
||||||
|
* Errors found during the validation can be retrieved via {@link getErrors}.
|
||||||
|
*
|
||||||
|
* @param array $attributes list of attributes that should be validated. Defaults to null,
|
||||||
|
* meaning any attribute listed in the applicable validation rules should be
|
||||||
|
* validated. If this parameter is given as a list of attributes, only
|
||||||
|
* the listed attributes will be validated.
|
||||||
|
* @param boolean $clearErrors whether to call {@link clearErrors} before performing validation
|
||||||
|
* @return boolean whether the validation is successful without any error.
|
||||||
|
* @see beforeValidate
|
||||||
|
* @see afterValidate
|
||||||
|
*/
|
||||||
|
public function validate($attributes=null, $clearErrors=true)
|
||||||
|
{
|
||||||
|
if($clearErrors)
|
||||||
|
$this->clearErrors();
|
||||||
|
if($this->beforeValidate())
|
||||||
|
{
|
||||||
|
foreach($this->getValidators() as $validator)
|
||||||
|
$validator->validate($this,$attributes);
|
||||||
|
$this->afterValidate();
|
||||||
|
return !$this->hasErrors();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked after a model instance is created by new operator.
|
||||||
|
* The default implementation raises the {@link onAfterConstruct} event.
|
||||||
|
* You may override this method to do postprocessing after model creation.
|
||||||
|
* Make sure you call the parent implementation so that the event is raised properly.
|
||||||
|
*/
|
||||||
|
protected function afterConstruct()
|
||||||
|
{
|
||||||
|
if($this->hasEventHandler('onAfterConstruct'))
|
||||||
|
$this->onAfterConstruct(new CEvent($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked before validation starts.
|
||||||
|
* The default implementation calls {@link onBeforeValidate} to raise an event.
|
||||||
|
* You may override this method to do preliminary checks before validation.
|
||||||
|
* Make sure the parent implementation is invoked so that the event can be raised.
|
||||||
|
* @return boolean whether validation should be executed. Defaults to true.
|
||||||
|
* If false is returned, the validation will stop and the model is considered invalid.
|
||||||
|
*/
|
||||||
|
protected function beforeValidate()
|
||||||
|
{
|
||||||
|
$event=new CModelEvent($this);
|
||||||
|
$this->onBeforeValidate($event);
|
||||||
|
return $event->isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked after validation ends.
|
||||||
|
* The default implementation calls {@link onAfterValidate} to raise an event.
|
||||||
|
* You may override this method to do postprocessing after validation.
|
||||||
|
* Make sure the parent implementation is invoked so that the event can be raised.
|
||||||
|
*/
|
||||||
|
protected function afterValidate()
|
||||||
|
{
|
||||||
|
$this->onAfterValidate(new CEvent($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is raised after the model instance is created by new operator.
|
||||||
|
* @param CEvent $event the event parameter
|
||||||
|
*/
|
||||||
|
public function onAfterConstruct($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onAfterConstruct',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is raised before the validation is performed.
|
||||||
|
* @param CModelEvent $event the event parameter
|
||||||
|
*/
|
||||||
|
public function onBeforeValidate($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onBeforeValidate',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This event is raised after the validation is performed.
|
||||||
|
* @param CEvent $event the event parameter
|
||||||
|
*/
|
||||||
|
public function onAfterValidate($event)
|
||||||
|
{
|
||||||
|
$this->raiseEvent('onAfterValidate',$event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all the validators declared in the model.
|
||||||
|
* This method differs from {@link getValidators} in that the latter
|
||||||
|
* would only return the validators applicable to the current {@link scenario}.
|
||||||
|
* Also, since this method return a {@link CList} object, you may
|
||||||
|
* manipulate it by inserting or removing validators (useful in behaviors).
|
||||||
|
* For example, <code>$model->validatorList->add($newValidator)</code>.
|
||||||
|
* The change made to the {@link CList} object will persist and reflect
|
||||||
|
* in the result of the next call of {@link getValidators}.
|
||||||
|
* @return CList all the validators declared in the model.
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public function getValidatorList()
|
||||||
|
{
|
||||||
|
if($this->_validators===null)
|
||||||
|
$this->_validators=$this->createValidators();
|
||||||
|
return $this->_validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the validators applicable to the current {@link scenario}.
|
||||||
|
* @param string $attribute the name of the attribute whose validators should be returned.
|
||||||
|
* If this is null, the validators for ALL attributes in the model will be returned.
|
||||||
|
* @return array the validators applicable to the current {@link scenario}.
|
||||||
|
*/
|
||||||
|
public function getValidators($attribute=null)
|
||||||
|
{
|
||||||
|
if($this->_validators===null)
|
||||||
|
$this->_validators=$this->createValidators();
|
||||||
|
|
||||||
|
$validators=array();
|
||||||
|
$scenario=$this->getScenario();
|
||||||
|
foreach($this->_validators as $validator)
|
||||||
|
{
|
||||||
|
if($validator->applyTo($scenario))
|
||||||
|
{
|
||||||
|
if($attribute===null || in_array($attribute,$validator->attributes,true))
|
||||||
|
$validators[]=$validator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates validator objects based on the specification in {@link rules}.
|
||||||
|
* This method is mainly used internally.
|
||||||
|
* @throws CException if current class has an invalid validation rule
|
||||||
|
* @return CList validators built based on {@link rules()}.
|
||||||
|
*/
|
||||||
|
public function createValidators()
|
||||||
|
{
|
||||||
|
$validators=new CList;
|
||||||
|
foreach($this->rules() as $rule)
|
||||||
|
{
|
||||||
|
if(isset($rule[0],$rule[1])) // attributes, validator name
|
||||||
|
$validators->add(CValidator::createValidator($rule[1],$this,$rule[0],array_slice($rule,2)));
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','{class} has an invalid validation rule. The rule must specify attributes to be validated and the validator name.',
|
||||||
|
array('{class}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
return $validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the attribute is required.
|
||||||
|
* This is determined by checking if the attribute is associated with a
|
||||||
|
* {@link CRequiredValidator} validation rule in the current {@link scenario}.
|
||||||
|
* @param string $attribute attribute name
|
||||||
|
* @return boolean whether the attribute is required
|
||||||
|
*/
|
||||||
|
public function isAttributeRequired($attribute)
|
||||||
|
{
|
||||||
|
foreach($this->getValidators($attribute) as $validator)
|
||||||
|
{
|
||||||
|
if($validator instanceof CRequiredValidator)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the attribute is safe for massive assignments.
|
||||||
|
* @param string $attribute attribute name
|
||||||
|
* @return boolean whether the attribute is safe for massive assignments
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public function isAttributeSafe($attribute)
|
||||||
|
{
|
||||||
|
$attributes=$this->getSafeAttributeNames();
|
||||||
|
return in_array($attribute,$attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the text label for the specified attribute.
|
||||||
|
* @param string $attribute the attribute name
|
||||||
|
* @return string the attribute label
|
||||||
|
* @see generateAttributeLabel
|
||||||
|
* @see attributeLabels
|
||||||
|
*/
|
||||||
|
public function getAttributeLabel($attribute)
|
||||||
|
{
|
||||||
|
$labels=$this->attributeLabels();
|
||||||
|
if(isset($labels[$attribute]))
|
||||||
|
return $labels[$attribute];
|
||||||
|
else
|
||||||
|
return $this->generateAttributeLabel($attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether there is any validation error.
|
||||||
|
* @param string $attribute attribute name. Use null to check all attributes.
|
||||||
|
* @return boolean whether there is any error.
|
||||||
|
*/
|
||||||
|
public function hasErrors($attribute=null)
|
||||||
|
{
|
||||||
|
if($attribute===null)
|
||||||
|
return $this->_errors!==array();
|
||||||
|
else
|
||||||
|
return isset($this->_errors[$attribute]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the errors for all attribute or a single attribute.
|
||||||
|
* @param string $attribute attribute name. Use null to retrieve errors for all attributes.
|
||||||
|
* @return array errors for all attributes or the specified attribute. Empty array is returned if no error.
|
||||||
|
*/
|
||||||
|
public function getErrors($attribute=null)
|
||||||
|
{
|
||||||
|
if($attribute===null)
|
||||||
|
return $this->_errors;
|
||||||
|
else
|
||||||
|
return isset($this->_errors[$attribute]) ? $this->_errors[$attribute] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first error of the specified attribute.
|
||||||
|
* @param string $attribute attribute name.
|
||||||
|
* @return string the error message. Null is returned if no error.
|
||||||
|
*/
|
||||||
|
public function getError($attribute)
|
||||||
|
{
|
||||||
|
return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new error to the specified attribute.
|
||||||
|
* @param string $attribute attribute name
|
||||||
|
* @param string $error new error message
|
||||||
|
*/
|
||||||
|
public function addError($attribute,$error)
|
||||||
|
{
|
||||||
|
$this->_errors[$attribute][]=$error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a list of errors.
|
||||||
|
* @param array $errors a list of errors. The array keys must be attribute names.
|
||||||
|
* The array values should be error messages. If an attribute has multiple errors,
|
||||||
|
* these errors must be given in terms of an array.
|
||||||
|
* You may use the result of {@link getErrors} as the value for this parameter.
|
||||||
|
*/
|
||||||
|
public function addErrors($errors)
|
||||||
|
{
|
||||||
|
foreach($errors as $attribute=>$error)
|
||||||
|
{
|
||||||
|
if(is_array($error))
|
||||||
|
{
|
||||||
|
foreach($error as $e)
|
||||||
|
$this->addError($attribute, $e);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$this->addError($attribute, $error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes errors for all attributes or a single attribute.
|
||||||
|
* @param string $attribute attribute name. Use null to remove errors for all attribute.
|
||||||
|
*/
|
||||||
|
public function clearErrors($attribute=null)
|
||||||
|
{
|
||||||
|
if($attribute===null)
|
||||||
|
$this->_errors=array();
|
||||||
|
else
|
||||||
|
unset($this->_errors[$attribute]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a user friendly attribute label.
|
||||||
|
* This is done by replacing underscores or dashes with blanks and
|
||||||
|
* changing the first letter of each word to upper case.
|
||||||
|
* For example, 'department_name' or 'DepartmentName' becomes 'Department Name'.
|
||||||
|
* @param string $name the column name
|
||||||
|
* @return string the attribute label
|
||||||
|
*/
|
||||||
|
public function generateAttributeLabel($name)
|
||||||
|
{
|
||||||
|
return ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $name)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all attribute values.
|
||||||
|
* @param array $names list of attributes whose value needs to be returned.
|
||||||
|
* Defaults to null, meaning all attributes as listed in {@link attributeNames} will be returned.
|
||||||
|
* If it is an array, only the attributes in the array will be returned.
|
||||||
|
* @return array attribute values (name=>value).
|
||||||
|
*/
|
||||||
|
public function getAttributes($names=null)
|
||||||
|
{
|
||||||
|
$values=array();
|
||||||
|
foreach($this->attributeNames() as $name)
|
||||||
|
$values[$name]=$this->$name;
|
||||||
|
|
||||||
|
if(is_array($names))
|
||||||
|
{
|
||||||
|
$values2=array();
|
||||||
|
foreach($names as $name)
|
||||||
|
$values2[$name]=isset($values[$name]) ? $values[$name] : null;
|
||||||
|
return $values2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the attribute values in a massive way.
|
||||||
|
* @param array $values attribute values (name=>value) to be set.
|
||||||
|
* @param boolean $safeOnly whether the assignments should only be done to the safe attributes.
|
||||||
|
* A safe attribute is one that is associated with a validation rule in the current {@link scenario}.
|
||||||
|
* @see getSafeAttributeNames
|
||||||
|
* @see attributeNames
|
||||||
|
*/
|
||||||
|
public function setAttributes($values,$safeOnly=true)
|
||||||
|
{
|
||||||
|
if(!is_array($values))
|
||||||
|
return;
|
||||||
|
$attributes=array_flip($safeOnly ? $this->getSafeAttributeNames() : $this->attributeNames());
|
||||||
|
foreach($values as $name=>$value)
|
||||||
|
{
|
||||||
|
if(isset($attributes[$name]))
|
||||||
|
$this->$name=$value;
|
||||||
|
elseif($safeOnly)
|
||||||
|
$this->onUnsafeAttribute($name,$value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the attributes to be null.
|
||||||
|
* @param array $names list of attributes to be set null. If this parameter is not given,
|
||||||
|
* all attributes as specified by {@link attributeNames} will have their values unset.
|
||||||
|
* @since 1.1.3
|
||||||
|
*/
|
||||||
|
public function unsetAttributes($names=null)
|
||||||
|
{
|
||||||
|
if($names===null)
|
||||||
|
$names=$this->attributeNames();
|
||||||
|
foreach($names as $name)
|
||||||
|
$this->$name=null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is invoked when an unsafe attribute is being massively assigned.
|
||||||
|
* The default implementation will log a warning message if YII_DEBUG is on.
|
||||||
|
* It does nothing otherwise.
|
||||||
|
* @param string $name the unsafe attribute name
|
||||||
|
* @param mixed $value the attribute value
|
||||||
|
* @since 1.1.1
|
||||||
|
*/
|
||||||
|
public function onUnsafeAttribute($name,$value)
|
||||||
|
{
|
||||||
|
if(YII_DEBUG)
|
||||||
|
Yii::log(Yii::t('yii','Failed to set unsafe attribute "{attribute}" of "{class}".',array('{attribute}'=>$name, '{class}'=>get_class($this))),CLogger::LEVEL_WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scenario that this model is used in.
|
||||||
|
*
|
||||||
|
* Scenario affects how validation is performed and which attributes can
|
||||||
|
* be massively assigned.
|
||||||
|
*
|
||||||
|
* A validation rule will be performed when calling {@link validate()}
|
||||||
|
* if its 'except' value does not contain current scenario value while
|
||||||
|
* 'on' option is not set or contains the current scenario value.
|
||||||
|
*
|
||||||
|
* And an attribute can be massively assigned if it is associated with
|
||||||
|
* a validation rule for the current scenario. Note that an exception is
|
||||||
|
* the {@link CUnsafeValidator unsafe} validator which marks the associated
|
||||||
|
* attributes as unsafe and not allowed to be massively assigned.
|
||||||
|
*
|
||||||
|
* @return string the scenario that this model is in.
|
||||||
|
*/
|
||||||
|
public function getScenario()
|
||||||
|
{
|
||||||
|
return $this->_scenario;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the scenario for the model.
|
||||||
|
* @param string $value the scenario that this model is in.
|
||||||
|
* @see getScenario
|
||||||
|
*/
|
||||||
|
public function setScenario($value)
|
||||||
|
{
|
||||||
|
$this->_scenario=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the attribute names that are safe to be massively assigned.
|
||||||
|
* A safe attribute is one that is associated with a validation rule in the current {@link scenario}.
|
||||||
|
* @return array safe attribute names
|
||||||
|
*/
|
||||||
|
public function getSafeAttributeNames()
|
||||||
|
{
|
||||||
|
$attributes=array();
|
||||||
|
$unsafe=array();
|
||||||
|
foreach($this->getValidators() as $validator)
|
||||||
|
{
|
||||||
|
if(!$validator->safe)
|
||||||
|
{
|
||||||
|
foreach($validator->attributes as $name)
|
||||||
|
$unsafe[]=$name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($validator->attributes as $name)
|
||||||
|
$attributes[$name]=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($unsafe as $name)
|
||||||
|
unset($attributes[$name]);
|
||||||
|
return array_keys($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator for traversing the attributes in the model.
|
||||||
|
* This method is required by the interface IteratorAggregate.
|
||||||
|
* @return CMapIterator an iterator for traversing the items in the list.
|
||||||
|
*/
|
||||||
|
public function getIterator()
|
||||||
|
{
|
||||||
|
$attributes=$this->getAttributes();
|
||||||
|
return new CMapIterator($attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is an element at the specified offset.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param mixed $offset the offset to check on
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function offsetExists($offset)
|
||||||
|
{
|
||||||
|
return property_exists($this,$offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the element at the specified offset.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param integer $offset the offset to retrieve element.
|
||||||
|
* @return mixed the element at the offset, null if no element is found at the offset
|
||||||
|
*/
|
||||||
|
public function offsetGet($offset)
|
||||||
|
{
|
||||||
|
return $this->$offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the element at the specified offset.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param integer $offset the offset to set element
|
||||||
|
* @param mixed $item the element value
|
||||||
|
*/
|
||||||
|
public function offsetSet($offset,$item)
|
||||||
|
{
|
||||||
|
$this->$offset=$item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsets the element at the specified offset.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param mixed $offset the offset to unset element
|
||||||
|
*/
|
||||||
|
public function offsetUnset($offset)
|
||||||
|
{
|
||||||
|
unset($this->$offset);
|
||||||
|
}
|
||||||
|
}
|
68
framework/base/CModelBehavior.php
Normal file
68
framework/base/CModelBehavior.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CModelBehavior class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CModelBehavior is a base class for behaviors that are attached to a model component.
|
||||||
|
* The model should extend from {@link CModel} or its child classes.
|
||||||
|
*
|
||||||
|
* @property CModel $owner The owner model that this behavior is attached to.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
*/
|
||||||
|
class CModelBehavior extends CBehavior
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Declares events and the corresponding event handler methods.
|
||||||
|
* The default implementation returns 'onAfterConstruct', 'onBeforeValidate' and 'onAfterValidate' events and handlers.
|
||||||
|
* If you override this method, make sure you merge the parent result to the return value.
|
||||||
|
* @return array events (array keys) and the corresponding event handler methods (array values).
|
||||||
|
* @see CBehavior::events
|
||||||
|
*/
|
||||||
|
public function events()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'onAfterConstruct'=>'afterConstruct',
|
||||||
|
'onBeforeValidate'=>'beforeValidate',
|
||||||
|
'onAfterValidate'=>'afterValidate',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to {@link CModel::onAfterConstruct} event.
|
||||||
|
* Override this method and make it public if you want to handle the corresponding event
|
||||||
|
* of the {@link CBehavior::owner owner}.
|
||||||
|
* @param CEvent $event event parameter
|
||||||
|
*/
|
||||||
|
protected function afterConstruct($event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to {@link CModel::onBeforeValidate} event.
|
||||||
|
* Override this method and make it public if you want to handle the corresponding event
|
||||||
|
* of the {@link owner}.
|
||||||
|
* You may set {@link CModelEvent::isValid} to be false to quit the validation process.
|
||||||
|
* @param CModelEvent $event event parameter
|
||||||
|
*/
|
||||||
|
protected function beforeValidate($event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responds to {@link CModel::onAfterValidate} event.
|
||||||
|
* Override this method and make it public if you want to handle the corresponding event
|
||||||
|
* of the {@link owner}.
|
||||||
|
* @param CEvent $event event parameter
|
||||||
|
*/
|
||||||
|
protected function afterValidate($event)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
31
framework/base/CModelEvent.php
Normal file
31
framework/base/CModelEvent.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CModelEvent class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CModelEvent class.
|
||||||
|
*
|
||||||
|
* CModelEvent represents the event parameters needed by events raised by a model.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CModelEvent extends CEvent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var boolean whether the model is in valid status and should continue its normal method execution cycles. Defaults to true.
|
||||||
|
* For example, when this event is raised in a {@link CFormModel} object that is executing {@link CModel::beforeValidate},
|
||||||
|
* if this property is set false by the event handler, the {@link CModel::validate} method will quit after handling this event.
|
||||||
|
* If true, the normal execution cycles will continue, including performing the real validations and calling
|
||||||
|
* {@link CModel::afterValidate}.
|
||||||
|
*/
|
||||||
|
public $isValid=true;
|
||||||
|
}
|
547
framework/base/CModule.php
Normal file
547
framework/base/CModule.php
Normal file
@ -0,0 +1,547 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CModule class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CModule is the base class for module and application classes.
|
||||||
|
*
|
||||||
|
* CModule mainly manages application components and sub-modules.
|
||||||
|
*
|
||||||
|
* @property string $id The module ID.
|
||||||
|
* @property string $basePath The root directory of the module. Defaults to the directory containing the module class.
|
||||||
|
* @property CAttributeCollection $params The list of user-defined parameters.
|
||||||
|
* @property string $modulePath The directory that contains the application modules. Defaults to the 'modules' subdirectory of {@link basePath}.
|
||||||
|
* @property CModule $parentModule The parent module. Null if this module does not have a parent.
|
||||||
|
* @property array $modules The configuration of the currently installed modules (module ID => configuration).
|
||||||
|
* @property array $components The application components (indexed by their IDs).
|
||||||
|
* @property array $import List of aliases to be imported.
|
||||||
|
* @property array $aliases List of aliases to be defined. The array keys are root aliases,
|
||||||
|
* while the array values are paths or aliases corresponding to the root aliases.
|
||||||
|
* For example,
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'models'=>'application.models', // an existing alias
|
||||||
|
* 'extensions'=>'application.extensions', // an existing alias
|
||||||
|
* 'backend'=>dirname(__FILE__).'/../backend', // a directory
|
||||||
|
* )
|
||||||
|
* </pre>.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
*/
|
||||||
|
abstract class CModule extends CComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var array the IDs of the application components that should be preloaded.
|
||||||
|
*/
|
||||||
|
public $preload=array();
|
||||||
|
/**
|
||||||
|
* @var array the behaviors that should be attached to the module.
|
||||||
|
* The behaviors will be attached to the module when {@link init} is called.
|
||||||
|
* Please refer to {@link CModel::behaviors} on how to specify the value of this property.
|
||||||
|
*/
|
||||||
|
public $behaviors=array();
|
||||||
|
|
||||||
|
private $_id;
|
||||||
|
private $_parentModule;
|
||||||
|
private $_basePath;
|
||||||
|
private $_modulePath;
|
||||||
|
private $_params;
|
||||||
|
private $_modules=array();
|
||||||
|
private $_moduleConfig=array();
|
||||||
|
private $_components=array();
|
||||||
|
private $_componentConfig=array();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $id the ID of this module
|
||||||
|
* @param CModule $parent the parent module (if any)
|
||||||
|
* @param mixed $config the module configuration. It can be either an array or
|
||||||
|
* the path of a PHP file returning the configuration array.
|
||||||
|
*/
|
||||||
|
public function __construct($id,$parent,$config=null)
|
||||||
|
{
|
||||||
|
$this->_id=$id;
|
||||||
|
$this->_parentModule=$parent;
|
||||||
|
|
||||||
|
// set basePath at early as possible to avoid trouble
|
||||||
|
if(is_string($config))
|
||||||
|
$config=require($config);
|
||||||
|
if(isset($config['basePath']))
|
||||||
|
{
|
||||||
|
$this->setBasePath($config['basePath']);
|
||||||
|
unset($config['basePath']);
|
||||||
|
}
|
||||||
|
Yii::setPathOfAlias($id,$this->getBasePath());
|
||||||
|
|
||||||
|
$this->preinit();
|
||||||
|
|
||||||
|
$this->configure($config);
|
||||||
|
$this->attachBehaviors($this->behaviors);
|
||||||
|
$this->preloadComponents();
|
||||||
|
|
||||||
|
$this->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter magic method.
|
||||||
|
* This method is overridden to support accessing application components
|
||||||
|
* like reading module properties.
|
||||||
|
* @param string $name application component or property name
|
||||||
|
* @return mixed the named property value
|
||||||
|
*/
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if($this->hasComponent($name))
|
||||||
|
return $this->getComponent($name);
|
||||||
|
else
|
||||||
|
return parent::__get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a property value is null.
|
||||||
|
* This method overrides the parent implementation by checking
|
||||||
|
* if the named application component is loaded.
|
||||||
|
* @param string $name the property name or the event name
|
||||||
|
* @return boolean whether the property value is null
|
||||||
|
*/
|
||||||
|
public function __isset($name)
|
||||||
|
{
|
||||||
|
if($this->hasComponent($name))
|
||||||
|
return $this->getComponent($name)!==null;
|
||||||
|
else
|
||||||
|
return parent::__isset($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the module ID.
|
||||||
|
* @return string the module ID.
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the module ID.
|
||||||
|
* @param string $id the module ID
|
||||||
|
*/
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->_id=$id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root directory of the module.
|
||||||
|
* @return string the root directory of the module. Defaults to the directory containing the module class.
|
||||||
|
*/
|
||||||
|
public function getBasePath()
|
||||||
|
{
|
||||||
|
if($this->_basePath===null)
|
||||||
|
{
|
||||||
|
$class=new ReflectionClass(get_class($this));
|
||||||
|
$this->_basePath=dirname($class->getFileName());
|
||||||
|
}
|
||||||
|
return $this->_basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the root directory of the module.
|
||||||
|
* This method can only be invoked at the beginning of the constructor.
|
||||||
|
* @param string $path the root directory of the module.
|
||||||
|
* @throws CException if the directory does not exist.
|
||||||
|
*/
|
||||||
|
public function setBasePath($path)
|
||||||
|
{
|
||||||
|
if(($this->_basePath=realpath($path))===false || !is_dir($this->_basePath))
|
||||||
|
throw new CException(Yii::t('yii','Base path "{path}" is not a valid directory.',
|
||||||
|
array('{path}'=>$path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns user-defined parameters.
|
||||||
|
* @return CAttributeCollection the list of user-defined parameters
|
||||||
|
*/
|
||||||
|
public function getParams()
|
||||||
|
{
|
||||||
|
if($this->_params!==null)
|
||||||
|
return $this->_params;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->_params=new CAttributeCollection;
|
||||||
|
$this->_params->caseSensitive=true;
|
||||||
|
return $this->_params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets user-defined parameters.
|
||||||
|
* @param array $value user-defined parameters. This should be in name-value pairs.
|
||||||
|
*/
|
||||||
|
public function setParams($value)
|
||||||
|
{
|
||||||
|
$params=$this->getParams();
|
||||||
|
foreach($value as $k=>$v)
|
||||||
|
$params->add($k,$v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the directory that contains the application modules.
|
||||||
|
* @return string the directory that contains the application modules. Defaults to the 'modules' subdirectory of {@link basePath}.
|
||||||
|
*/
|
||||||
|
public function getModulePath()
|
||||||
|
{
|
||||||
|
if($this->_modulePath!==null)
|
||||||
|
return $this->_modulePath;
|
||||||
|
else
|
||||||
|
return $this->_modulePath=$this->getBasePath().DIRECTORY_SEPARATOR.'modules';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the directory that contains the application modules.
|
||||||
|
* @param string $value the directory that contains the application modules.
|
||||||
|
* @throws CException if the directory is invalid
|
||||||
|
*/
|
||||||
|
public function setModulePath($value)
|
||||||
|
{
|
||||||
|
if(($this->_modulePath=realpath($value))===false || !is_dir($this->_modulePath))
|
||||||
|
throw new CException(Yii::t('yii','The module path "{path}" is not a valid directory.',
|
||||||
|
array('{path}'=>$value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the aliases that are used in the module.
|
||||||
|
* @param array $aliases list of aliases to be imported
|
||||||
|
*/
|
||||||
|
public function setImport($aliases)
|
||||||
|
{
|
||||||
|
foreach($aliases as $alias)
|
||||||
|
Yii::import($alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the root aliases.
|
||||||
|
* @param array $mappings list of aliases to be defined. The array keys are root aliases,
|
||||||
|
* while the array values are paths or aliases corresponding to the root aliases.
|
||||||
|
* For example,
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'models'=>'application.models', // an existing alias
|
||||||
|
* 'extensions'=>'application.extensions', // an existing alias
|
||||||
|
* 'backend'=>dirname(__FILE__).'/../backend', // a directory
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public function setAliases($mappings)
|
||||||
|
{
|
||||||
|
foreach($mappings as $name=>$alias)
|
||||||
|
{
|
||||||
|
if(($path=Yii::getPathOfAlias($alias))!==false)
|
||||||
|
Yii::setPathOfAlias($name,$path);
|
||||||
|
else
|
||||||
|
Yii::setPathOfAlias($name,$alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent module.
|
||||||
|
* @return CModule the parent module. Null if this module does not have a parent.
|
||||||
|
*/
|
||||||
|
public function getParentModule()
|
||||||
|
{
|
||||||
|
return $this->_parentModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the named application module.
|
||||||
|
* The module has to be declared in {@link modules}. A new instance will be created
|
||||||
|
* when calling this method with the given ID for the first time.
|
||||||
|
* @param string $id application module ID (case-sensitive)
|
||||||
|
* @return CModule the module instance, null if the module is disabled or does not exist.
|
||||||
|
*/
|
||||||
|
public function getModule($id)
|
||||||
|
{
|
||||||
|
if(isset($this->_modules[$id]) || array_key_exists($id,$this->_modules))
|
||||||
|
return $this->_modules[$id];
|
||||||
|
elseif(isset($this->_moduleConfig[$id]))
|
||||||
|
{
|
||||||
|
$config=$this->_moduleConfig[$id];
|
||||||
|
if(!isset($config['enabled']) || $config['enabled'])
|
||||||
|
{
|
||||||
|
Yii::trace("Loading \"$id\" module",'system.base.CModule');
|
||||||
|
$class=$config['class'];
|
||||||
|
unset($config['class'], $config['enabled']);
|
||||||
|
if($this===Yii::app())
|
||||||
|
$module=Yii::createComponent($class,$id,null,$config);
|
||||||
|
else
|
||||||
|
$module=Yii::createComponent($class,$this->getId().'/'.$id,$this,$config);
|
||||||
|
return $this->_modules[$id]=$module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the specified module is installed.
|
||||||
|
* @param string $id the module ID
|
||||||
|
* @return boolean whether the specified module is installed.
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
public function hasModule($id)
|
||||||
|
{
|
||||||
|
return isset($this->_moduleConfig[$id]) || isset($this->_modules[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the configuration of the currently installed modules.
|
||||||
|
* @return array the configuration of the currently installed modules (module ID => configuration)
|
||||||
|
*/
|
||||||
|
public function getModules()
|
||||||
|
{
|
||||||
|
return $this->_moduleConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the sub-modules of this module.
|
||||||
|
*
|
||||||
|
* Call this method to declare sub-modules and configure them with their initial property values.
|
||||||
|
* The parameter should be an array of module configurations. Each array element represents a single module,
|
||||||
|
* which can be either a string representing the module ID or an ID-configuration pair representing
|
||||||
|
* a module with the specified ID and the initial property values.
|
||||||
|
*
|
||||||
|
* For example, the following array declares two modules:
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'admin', // a single module ID
|
||||||
|
* 'payment'=>array( // ID-configuration pair
|
||||||
|
* 'server'=>'paymentserver.com',
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* By default, the module class is determined using the expression <code>ucfirst($moduleID).'Module'</code>.
|
||||||
|
* And the class file is located under <code>modules/$moduleID</code>.
|
||||||
|
* You may override this default by explicitly specifying the 'class' option in the configuration.
|
||||||
|
*
|
||||||
|
* You may also enable or disable a module by specifying the 'enabled' option in the configuration.
|
||||||
|
*
|
||||||
|
* @param array $modules module configurations.
|
||||||
|
*/
|
||||||
|
public function setModules($modules)
|
||||||
|
{
|
||||||
|
foreach($modules as $id=>$module)
|
||||||
|
{
|
||||||
|
if(is_int($id))
|
||||||
|
{
|
||||||
|
$id=$module;
|
||||||
|
$module=array();
|
||||||
|
}
|
||||||
|
if(!isset($module['class']))
|
||||||
|
{
|
||||||
|
Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id);
|
||||||
|
$module['class']=$id.'.'.ucfirst($id).'Module';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($this->_moduleConfig[$id]))
|
||||||
|
$this->_moduleConfig[$id]=CMap::mergeArray($this->_moduleConfig[$id],$module);
|
||||||
|
else
|
||||||
|
$this->_moduleConfig[$id]=$module;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the named component exists.
|
||||||
|
* @param string $id application component ID
|
||||||
|
* @return boolean whether the named application component exists (including both loaded and disabled.)
|
||||||
|
*/
|
||||||
|
public function hasComponent($id)
|
||||||
|
{
|
||||||
|
return isset($this->_components[$id]) || isset($this->_componentConfig[$id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the named application component.
|
||||||
|
* @param string $id application component ID (case-sensitive)
|
||||||
|
* @param boolean $createIfNull whether to create the component if it doesn't exist yet.
|
||||||
|
* @return IApplicationComponent the application component instance, null if the application component is disabled or does not exist.
|
||||||
|
* @see hasComponent
|
||||||
|
*/
|
||||||
|
public function getComponent($id,$createIfNull=true)
|
||||||
|
{
|
||||||
|
if(isset($this->_components[$id]))
|
||||||
|
return $this->_components[$id];
|
||||||
|
elseif(isset($this->_componentConfig[$id]) && $createIfNull)
|
||||||
|
{
|
||||||
|
$config=$this->_componentConfig[$id];
|
||||||
|
if(!isset($config['enabled']) || $config['enabled'])
|
||||||
|
{
|
||||||
|
Yii::trace("Loading \"$id\" application component",'system.CModule');
|
||||||
|
unset($config['enabled']);
|
||||||
|
$component=Yii::createComponent($config);
|
||||||
|
$component->init();
|
||||||
|
return $this->_components[$id]=$component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a component under the management of the module.
|
||||||
|
* The component will be initialized by calling its {@link CApplicationComponent::init() init()}
|
||||||
|
* method if it has not done so.
|
||||||
|
* @param string $id component ID
|
||||||
|
* @param array|IApplicationComponent $component application component
|
||||||
|
* (either configuration array or instance). If this parameter is null,
|
||||||
|
* component will be unloaded from the module.
|
||||||
|
* @param boolean $merge whether to merge the new component configuration
|
||||||
|
* with the existing one. Defaults to true, meaning the previously registered
|
||||||
|
* component configuration with the same ID will be merged with the new configuration.
|
||||||
|
* If set to false, the existing configuration will be replaced completely.
|
||||||
|
* This parameter is available since 1.1.13.
|
||||||
|
*/
|
||||||
|
public function setComponent($id,$component,$merge=true)
|
||||||
|
{
|
||||||
|
if($component===null)
|
||||||
|
{
|
||||||
|
unset($this->_components[$id]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elseif($component instanceof IApplicationComponent)
|
||||||
|
{
|
||||||
|
$this->_components[$id]=$component;
|
||||||
|
|
||||||
|
if(!$component->getIsInitialized())
|
||||||
|
$component->init();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elseif(isset($this->_components[$id]))
|
||||||
|
{
|
||||||
|
if(isset($component['class']) && get_class($this->_components[$id])!==$component['class'])
|
||||||
|
{
|
||||||
|
unset($this->_components[$id]);
|
||||||
|
$this->_componentConfig[$id]=$component; //we should ignore merge here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($component as $key=>$value)
|
||||||
|
{
|
||||||
|
if($key!=='class')
|
||||||
|
$this->_components[$id]->$key=$value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif(isset($this->_componentConfig[$id]['class'],$component['class'])
|
||||||
|
&& $this->_componentConfig[$id]['class']!==$component['class'])
|
||||||
|
{
|
||||||
|
$this->_componentConfig[$id]=$component; //we should ignore merge here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($this->_componentConfig[$id]) && $merge)
|
||||||
|
$this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
|
||||||
|
else
|
||||||
|
$this->_componentConfig[$id]=$component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the application components.
|
||||||
|
* @param boolean $loadedOnly whether to return the loaded components only. If this is set false,
|
||||||
|
* then all components specified in the configuration will be returned, whether they are loaded or not.
|
||||||
|
* Loaded components will be returned as objects, while unloaded components as configuration arrays.
|
||||||
|
* This parameter has been available since version 1.1.3.
|
||||||
|
* @return array the application components (indexed by their IDs)
|
||||||
|
*/
|
||||||
|
public function getComponents($loadedOnly=true)
|
||||||
|
{
|
||||||
|
if($loadedOnly)
|
||||||
|
return $this->_components;
|
||||||
|
else
|
||||||
|
return array_merge($this->_componentConfig, $this->_components);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the application components.
|
||||||
|
*
|
||||||
|
* When a configuration is used to specify a component, it should consist of
|
||||||
|
* the component's initial property values (name-value pairs). Additionally,
|
||||||
|
* a component can be enabled (default) or disabled by specifying the 'enabled' value
|
||||||
|
* in the configuration.
|
||||||
|
*
|
||||||
|
* If a configuration is specified with an ID that is the same as an existing
|
||||||
|
* component or configuration, the existing one will be replaced silently.
|
||||||
|
*
|
||||||
|
* The following is the configuration for two components:
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'db'=>array(
|
||||||
|
* 'class'=>'CDbConnection',
|
||||||
|
* 'connectionString'=>'sqlite:path/to/file.db',
|
||||||
|
* ),
|
||||||
|
* 'cache'=>array(
|
||||||
|
* 'class'=>'CDbCache',
|
||||||
|
* 'connectionID'=>'db',
|
||||||
|
* 'enabled'=>!YII_DEBUG, // enable caching in non-debug mode
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param array $components application components(id=>component configuration or instances)
|
||||||
|
* @param boolean $merge whether to merge the new component configuration with the existing one.
|
||||||
|
* Defaults to true, meaning the previously registered component configuration of the same ID
|
||||||
|
* will be merged with the new configuration. If false, the existing configuration will be replaced completely.
|
||||||
|
*/
|
||||||
|
public function setComponents($components,$merge=true)
|
||||||
|
{
|
||||||
|
foreach($components as $id=>$component)
|
||||||
|
$this->setComponent($id,$component,$merge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures the module with the specified configuration.
|
||||||
|
* @param array $config the configuration array
|
||||||
|
*/
|
||||||
|
public function configure($config)
|
||||||
|
{
|
||||||
|
if(is_array($config))
|
||||||
|
{
|
||||||
|
foreach($config as $key=>$value)
|
||||||
|
$this->$key=$value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads static application components.
|
||||||
|
*/
|
||||||
|
protected function preloadComponents()
|
||||||
|
{
|
||||||
|
foreach($this->preload as $id)
|
||||||
|
$this->getComponent($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preinitializes the module.
|
||||||
|
* This method is called at the beginning of the module constructor.
|
||||||
|
* You may override this method to do some customized preinitialization work.
|
||||||
|
* Note that at this moment, the module is not configured yet.
|
||||||
|
* @see init
|
||||||
|
*/
|
||||||
|
protected function preinit()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the module.
|
||||||
|
* This method is called at the end of the module constructor.
|
||||||
|
* Note that at this moment, the module has been configured, the behaviors
|
||||||
|
* have been attached and the application components have been registered.
|
||||||
|
* @see preinit
|
||||||
|
*/
|
||||||
|
protected function init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
492
framework/base/CSecurityManager.php
Normal file
492
framework/base/CSecurityManager.php
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains classes implementing security manager feature.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CSecurityManager provides private keys, hashing and encryption functions.
|
||||||
|
*
|
||||||
|
* CSecurityManager is used by Yii components and applications for security-related purpose.
|
||||||
|
* For example, it is used in cookie validation feature to prevent cookie data
|
||||||
|
* from being tampered.
|
||||||
|
*
|
||||||
|
* CSecurityManager is mainly used to protect data from being tampered and viewed.
|
||||||
|
* It can generate HMAC and encrypt the data. The private key used to generate HMAC
|
||||||
|
* is set by {@link setValidationKey ValidationKey}. The key used to encrypt data is
|
||||||
|
* specified by {@link setEncryptionKey EncryptionKey}. If the above keys are not
|
||||||
|
* explicitly set, random keys will be generated and used.
|
||||||
|
*
|
||||||
|
* To protected data with HMAC, call {@link hashData()}; and to check if the data
|
||||||
|
* is tampered, call {@link validateData()}, which will return the real data if
|
||||||
|
* it is not tampered. The algorithm used to generated HMAC is specified by
|
||||||
|
* {@link validation}.
|
||||||
|
*
|
||||||
|
* To encrypt and decrypt data, call {@link encrypt()} and {@link decrypt()}
|
||||||
|
* respectively, which uses 3DES encryption algorithm. Note, the PHP Mcrypt
|
||||||
|
* extension must be installed and loaded.
|
||||||
|
*
|
||||||
|
* CSecurityManager is a core application component that can be accessed via
|
||||||
|
* {@link CApplication::getSecurityManager()}.
|
||||||
|
*
|
||||||
|
* @property string $validationKey The private key used to generate HMAC.
|
||||||
|
* If the key is not explicitly set, a random one is generated and returned.
|
||||||
|
* @property string $encryptionKey The private key used to encrypt/decrypt data.
|
||||||
|
* If the key is not explicitly set, a random one is generated and returned.
|
||||||
|
* @property string $validation
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CSecurityManager extends CApplicationComponent
|
||||||
|
{
|
||||||
|
const STATE_VALIDATION_KEY='Yii.CSecurityManager.validationkey';
|
||||||
|
const STATE_ENCRYPTION_KEY='Yii.CSecurityManager.encryptionkey';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string the name of the hashing algorithm to be used by {@link computeHMAC}.
|
||||||
|
* See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible
|
||||||
|
* hash algorithms. Note that if you are using PHP 5.1.1 or below, you can only use 'sha1' or 'md5'.
|
||||||
|
*
|
||||||
|
* Defaults to 'sha1', meaning using SHA1 hash algorithm.
|
||||||
|
* @since 1.1.3
|
||||||
|
*/
|
||||||
|
public $hashAlgorithm='sha1';
|
||||||
|
/**
|
||||||
|
* @var mixed the name of the crypt algorithm to be used by {@link encrypt} and {@link decrypt}.
|
||||||
|
* This will be passed as the first parameter to {@link http://php.net/manual/en/function.mcrypt-module-open.php mcrypt_module_open}.
|
||||||
|
*
|
||||||
|
* This property can also be configured as an array. In this case, the array elements will be passed in order
|
||||||
|
* as parameters to mcrypt_module_open. For example, <code>array('rijndael-256', '', 'ofb', '')</code>.
|
||||||
|
*
|
||||||
|
* Defaults to 'des', meaning using DES crypt algorithm.
|
||||||
|
* @since 1.1.3
|
||||||
|
*/
|
||||||
|
public $cryptAlgorithm='des';
|
||||||
|
|
||||||
|
private $_validationKey;
|
||||||
|
private $_encryptionKey;
|
||||||
|
private $_mbstring;
|
||||||
|
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
$this->_mbstring=extension_loaded('mbstring');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string a randomly generated private key.
|
||||||
|
* @deprecated in favor of {@link generateRandomString()} since 1.1.14. Never use this method.
|
||||||
|
*/
|
||||||
|
protected function generateRandomKey()
|
||||||
|
{
|
||||||
|
return $this->generateRandomString(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the private key used to generate HMAC.
|
||||||
|
* If the key is not explicitly set, a random one is generated and returned.
|
||||||
|
* @throws CException in case random string cannot be generated.
|
||||||
|
*/
|
||||||
|
public function getValidationKey()
|
||||||
|
{
|
||||||
|
if($this->_validationKey!==null)
|
||||||
|
return $this->_validationKey;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($key=Yii::app()->getGlobalState(self::STATE_VALIDATION_KEY))!==null)
|
||||||
|
$this->setValidationKey($key);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($key=$this->generateRandomString(32,true))===false)
|
||||||
|
if(($key=$this->generateRandomString(32,false))===false)
|
||||||
|
throw new CException(Yii::t('yii',
|
||||||
|
'CSecurityManager::generateRandomString() cannot generate random string in the current environment.'));
|
||||||
|
$this->setValidationKey($key);
|
||||||
|
Yii::app()->setGlobalState(self::STATE_VALIDATION_KEY,$key);
|
||||||
|
}
|
||||||
|
return $this->_validationKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value the key used to generate HMAC
|
||||||
|
* @throws CException if the key is empty
|
||||||
|
*/
|
||||||
|
public function setValidationKey($value)
|
||||||
|
{
|
||||||
|
if(!empty($value))
|
||||||
|
$this->_validationKey=$value;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CSecurityManager.validationKey cannot be empty.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the private key used to encrypt/decrypt data.
|
||||||
|
* If the key is not explicitly set, a random one is generated and returned.
|
||||||
|
* @throws CException in case random string cannot be generated.
|
||||||
|
*/
|
||||||
|
public function getEncryptionKey()
|
||||||
|
{
|
||||||
|
if($this->_encryptionKey!==null)
|
||||||
|
return $this->_encryptionKey;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($key=Yii::app()->getGlobalState(self::STATE_ENCRYPTION_KEY))!==null)
|
||||||
|
$this->setEncryptionKey($key);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($key=$this->generateRandomString(32,true))===false)
|
||||||
|
if(($key=$this->generateRandomString(32,false))===false)
|
||||||
|
throw new CException(Yii::t('yii',
|
||||||
|
'CSecurityManager::generateRandomString() cannot generate random string in the current environment.'));
|
||||||
|
$this->setEncryptionKey($key);
|
||||||
|
Yii::app()->setGlobalState(self::STATE_ENCRYPTION_KEY,$key);
|
||||||
|
}
|
||||||
|
return $this->_encryptionKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $value the key used to encrypt/decrypt data.
|
||||||
|
* @throws CException if the key is empty
|
||||||
|
*/
|
||||||
|
public function setEncryptionKey($value)
|
||||||
|
{
|
||||||
|
if(!empty($value))
|
||||||
|
$this->_encryptionKey=$value;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CSecurityManager.encryptionKey cannot be empty.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has been deprecated since version 1.1.3.
|
||||||
|
* Please use {@link hashAlgorithm} instead.
|
||||||
|
* @return string -
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function getValidation()
|
||||||
|
{
|
||||||
|
return $this->hashAlgorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method has been deprecated since version 1.1.3.
|
||||||
|
* Please use {@link hashAlgorithm} instead.
|
||||||
|
* @param string $value -
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public function setValidation($value)
|
||||||
|
{
|
||||||
|
$this->hashAlgorithm=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts data.
|
||||||
|
* @param string $data data to be encrypted.
|
||||||
|
* @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
|
||||||
|
* @return string the encrypted data
|
||||||
|
* @throws CException if PHP Mcrypt extension is not loaded
|
||||||
|
*/
|
||||||
|
public function encrypt($data,$key=null)
|
||||||
|
{
|
||||||
|
$module=$this->openCryptModule();
|
||||||
|
$key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
|
||||||
|
srand();
|
||||||
|
$iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND);
|
||||||
|
mcrypt_generic_init($module,$key,$iv);
|
||||||
|
$encrypted=$iv.mcrypt_generic($module,$data);
|
||||||
|
mcrypt_generic_deinit($module);
|
||||||
|
mcrypt_module_close($module);
|
||||||
|
return $encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts data
|
||||||
|
* @param string $data data to be decrypted.
|
||||||
|
* @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}.
|
||||||
|
* @return string the decrypted data
|
||||||
|
* @throws CException if PHP Mcrypt extension is not loaded
|
||||||
|
*/
|
||||||
|
public function decrypt($data,$key=null)
|
||||||
|
{
|
||||||
|
$module=$this->openCryptModule();
|
||||||
|
$key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module));
|
||||||
|
$ivSize=mcrypt_enc_get_iv_size($module);
|
||||||
|
$iv=$this->substr($data,0,$ivSize);
|
||||||
|
mcrypt_generic_init($module,$key,$iv);
|
||||||
|
$decrypted=mdecrypt_generic($module,$this->substr($data,$ivSize,$this->strlen($data)));
|
||||||
|
mcrypt_generic_deinit($module);
|
||||||
|
mcrypt_module_close($module);
|
||||||
|
return rtrim($decrypted,"\0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the mcrypt module with the configuration specified in {@link cryptAlgorithm}.
|
||||||
|
* @throws CException if failed to initialize the mcrypt module or PHP mcrypt extension
|
||||||
|
* @return resource the mycrypt module handle.
|
||||||
|
* @since 1.1.3
|
||||||
|
*/
|
||||||
|
protected function openCryptModule()
|
||||||
|
{
|
||||||
|
if(extension_loaded('mcrypt'))
|
||||||
|
{
|
||||||
|
if(is_array($this->cryptAlgorithm))
|
||||||
|
$module=@call_user_func_array('mcrypt_module_open',$this->cryptAlgorithm);
|
||||||
|
else
|
||||||
|
$module=@mcrypt_module_open($this->cryptAlgorithm,'', MCRYPT_MODE_CBC,'');
|
||||||
|
|
||||||
|
if($module===false)
|
||||||
|
throw new CException(Yii::t('yii','Failed to initialize the mcrypt module.'));
|
||||||
|
|
||||||
|
return $module;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefixes data with an HMAC.
|
||||||
|
* @param string $data data to be hashed.
|
||||||
|
* @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
|
||||||
|
* @return string data prefixed with HMAC
|
||||||
|
*/
|
||||||
|
public function hashData($data,$key=null)
|
||||||
|
{
|
||||||
|
return $this->computeHMAC($data,$key).$data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if data is tampered.
|
||||||
|
* @param string $data data to be validated. The data must be previously
|
||||||
|
* generated using {@link hashData()}.
|
||||||
|
* @param string $key the private key to be used for generating HMAC. Defaults to null, meaning using {@link validationKey}.
|
||||||
|
* @return string the real data with HMAC stripped off. False if the data
|
||||||
|
* is tampered.
|
||||||
|
*/
|
||||||
|
public function validateData($data,$key=null)
|
||||||
|
{
|
||||||
|
$len=$this->strlen($this->computeHMAC('test'));
|
||||||
|
if($this->strlen($data)>=$len)
|
||||||
|
{
|
||||||
|
$hmac=$this->substr($data,0,$len);
|
||||||
|
$data2=$this->substr($data,$len,$this->strlen($data));
|
||||||
|
return $hmac===$this->computeHMAC($data2,$key)?$data2:false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the HMAC for the data with {@link getValidationKey validationKey}. This method has been made public
|
||||||
|
* since 1.1.14.
|
||||||
|
* @param string $data data to be generated HMAC.
|
||||||
|
* @param string|null $key the private key to be used for generating HMAC. Defaults to null, meaning using
|
||||||
|
* {@link validationKey} value.
|
||||||
|
* @param string|null $hashAlgorithm the name of the hashing algorithm to be used.
|
||||||
|
* See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible
|
||||||
|
* hash algorithms. Note that if you are using PHP 5.1.1 or below, you can only use 'sha1' or 'md5'.
|
||||||
|
* Defaults to null, meaning using {@link hashAlgorithm} value.
|
||||||
|
* @return string the HMAC for the data.
|
||||||
|
* @throws CException on unsupported hash algorithm given.
|
||||||
|
*/
|
||||||
|
public function computeHMAC($data,$key=null,$hashAlgorithm=null)
|
||||||
|
{
|
||||||
|
if($key===null)
|
||||||
|
$key=$this->getValidationKey();
|
||||||
|
if($hashAlgorithm===null)
|
||||||
|
$hashAlgorithm=$this->hashAlgorithm;
|
||||||
|
|
||||||
|
if(function_exists('hash_hmac'))
|
||||||
|
return hash_hmac($hashAlgorithm,$data,$key);
|
||||||
|
|
||||||
|
if(0===strcasecmp($hashAlgorithm,'sha1'))
|
||||||
|
{
|
||||||
|
$pack='H40';
|
||||||
|
$func='sha1';
|
||||||
|
}
|
||||||
|
elseif(0===strcasecmp($hashAlgorithm,'md5'))
|
||||||
|
{
|
||||||
|
$pack='H32';
|
||||||
|
$func='md5';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','Only SHA1 and MD5 hashing algorithms are supported when using PHP 5.1.1 or below.'));
|
||||||
|
}
|
||||||
|
if($this->strlen($key)>64)
|
||||||
|
$key=pack($pack,$func($key));
|
||||||
|
if($this->strlen($key)<64)
|
||||||
|
$key=str_pad($key,64,chr(0));
|
||||||
|
$key=$this->substr($key,0,64);
|
||||||
|
return $func((str_repeat(chr(0x5C), 64) ^ $key) . pack($pack, $func((str_repeat(chr(0x36), 64) ^ $key) . $data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random ASCII string. Generates only [0-9a-zA-z_~] characters which are all
|
||||||
|
* transparent in raw URL encoding.
|
||||||
|
* @param integer $length length of the generated string in characters.
|
||||||
|
* @param boolean $cryptographicallyStrong set this to require cryptographically strong randomness.
|
||||||
|
* @return string|boolean random string or false in case it cannot be generated.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public function generateRandomString($length,$cryptographicallyStrong=true)
|
||||||
|
{
|
||||||
|
if(($randomBytes=$this->generateRandomBytes($length+2,$cryptographicallyStrong))!==false)
|
||||||
|
return strtr($this->substr(base64_encode($randomBytes),0,$length),array('+'=>'_','/'=>'~'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a string of random bytes.
|
||||||
|
* @param integer $length number of random bytes to be generated.
|
||||||
|
* @param boolean $cryptographicallyStrong whether to fail if a cryptographically strong
|
||||||
|
* result cannot be generated. The method attempts to read from a cryptographically strong
|
||||||
|
* pseudorandom number generator (CS-PRNG), see
|
||||||
|
* {@link https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Requirements Wikipedia}.
|
||||||
|
* However, in some runtime environments, PHP has no access to a CS-PRNG, in which case
|
||||||
|
* the method returns false if $cryptographicallyStrong is true. When $cryptographicallyStrong is false,
|
||||||
|
* the method always returns a pseudorandom result but may fall back to using {@link generatePseudoRandomBlock}.
|
||||||
|
* This method does not guarantee that entropy, from sources external to the CS-PRNG, was mixed into
|
||||||
|
* the CS-PRNG state between each successive call. The caller can therefore expect non-blocking
|
||||||
|
* behavior, unlike, for example, reading from /dev/random on Linux, see
|
||||||
|
* {@link http://eprint.iacr.org/2006/086.pdf Gutterman et al 2006}.
|
||||||
|
* @return boolean|string generated random binary string or false on failure.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public function generateRandomBytes($length,$cryptographicallyStrong=true)
|
||||||
|
{
|
||||||
|
$bytes='';
|
||||||
|
if(function_exists('openssl_random_pseudo_bytes'))
|
||||||
|
{
|
||||||
|
$bytes=openssl_random_pseudo_bytes($length,$strong);
|
||||||
|
if($this->strlen($bytes)>=$length && ($strong || !$cryptographicallyStrong))
|
||||||
|
return $this->substr($bytes,0,$length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(function_exists('mcrypt_create_iv') &&
|
||||||
|
($bytes=mcrypt_create_iv($length, MCRYPT_DEV_URANDOM))!==false &&
|
||||||
|
$this->strlen($bytes)>=$length)
|
||||||
|
{
|
||||||
|
return $this->substr($bytes,0,$length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($file=@fopen('/dev/urandom','rb'))!==false &&
|
||||||
|
($bytes=@fread($file,$length))!==false &&
|
||||||
|
(fclose($file) || true) &&
|
||||||
|
$this->strlen($bytes)>=$length)
|
||||||
|
{
|
||||||
|
return $this->substr($bytes,0,$length);
|
||||||
|
}
|
||||||
|
|
||||||
|
$i=0;
|
||||||
|
while($this->strlen($bytes)<$length &&
|
||||||
|
($byte=$this->generateSessionRandomBlock())!==false &&
|
||||||
|
++$i<3)
|
||||||
|
{
|
||||||
|
$bytes.=$byte;
|
||||||
|
}
|
||||||
|
if($this->strlen($bytes)>=$length)
|
||||||
|
return $this->substr($bytes,0,$length);
|
||||||
|
|
||||||
|
if ($cryptographicallyStrong)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while($this->strlen($bytes)<$length)
|
||||||
|
$bytes.=$this->generatePseudoRandomBlock();
|
||||||
|
return $this->substr($bytes,0,$length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a pseudo random block of data using several sources. On some systems this may be a bit
|
||||||
|
* better than PHP's {@link mt_rand} built-in function, which is not really random.
|
||||||
|
* @return string of 64 pseudo random bytes.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public function generatePseudoRandomBlock()
|
||||||
|
{
|
||||||
|
$bytes='';
|
||||||
|
|
||||||
|
if (function_exists('openssl_random_pseudo_bytes')
|
||||||
|
&& ($bytes=openssl_random_pseudo_bytes(512))!==false
|
||||||
|
&& $this->strlen($bytes)>=512)
|
||||||
|
{
|
||||||
|
return $this->substr($bytes,0,512);
|
||||||
|
}
|
||||||
|
|
||||||
|
for($i=0;$i<32;++$i)
|
||||||
|
$bytes.=pack('S',mt_rand(0,0xffff));
|
||||||
|
|
||||||
|
// On UNIX and UNIX-like operating systems the numerical values in `ps`, `uptime` and `iostat`
|
||||||
|
// ought to be fairly unpredictable. Gather the non-zero digits from those.
|
||||||
|
foreach(array('ps','uptime','iostat') as $command) {
|
||||||
|
@exec($command,$commandResult,$retVal);
|
||||||
|
if(is_array($commandResult) && !empty($commandResult) && $retVal==0)
|
||||||
|
$bytes.=preg_replace('/[^1-9]/','',implode('',$commandResult));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather the current time's microsecond part. Note: this is only a source of entropy on
|
||||||
|
// the first call! If multiple calls are made, the entropy is only as much as the
|
||||||
|
// randomness in the time between calls.
|
||||||
|
$bytes.=$this->substr(microtime(),2,6);
|
||||||
|
|
||||||
|
// Concatenate everything gathered, mix it with sha512. hash() is part of PHP core and
|
||||||
|
// enabled by default but it can be disabled at compile time but we ignore that possibility here.
|
||||||
|
return hash('sha512',$bytes,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get random bytes from the system entropy source via PHP session manager.
|
||||||
|
* @return boolean|string 20-byte random binary string or false on error.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public function generateSessionRandomBlock()
|
||||||
|
{
|
||||||
|
ini_set('session.entropy_length',20);
|
||||||
|
if(ini_get('session.entropy_length')!=20)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// These calls are (supposed to be, according to PHP manual) safe even if
|
||||||
|
// there is already an active session for the calling script.
|
||||||
|
@session_start();
|
||||||
|
@session_regenerate_id();
|
||||||
|
|
||||||
|
$bytes=session_id();
|
||||||
|
if(!$bytes)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// $bytes has 20 bytes of entropy but the session manager converts the binary
|
||||||
|
// random bytes into something readable. We have to convert that back.
|
||||||
|
// SHA-1 should do it without losing entropy.
|
||||||
|
return sha1($bytes,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the given string.
|
||||||
|
* If available uses the multibyte string function mb_strlen.
|
||||||
|
* @param string $string the string being measured for length
|
||||||
|
* @return integer the length of the string
|
||||||
|
*/
|
||||||
|
private function strlen($string)
|
||||||
|
{
|
||||||
|
return $this->_mbstring ? mb_strlen($string,'8bit') : strlen($string);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the portion of string specified by the start and length parameters.
|
||||||
|
* If available uses the multibyte string function mb_substr
|
||||||
|
* @param string $string the input string. Must be one character or longer.
|
||||||
|
* @param integer $start the starting position
|
||||||
|
* @param integer $length the desired portion length
|
||||||
|
* @return string the extracted part of string, or FALSE on failure or an empty string.
|
||||||
|
*/
|
||||||
|
private function substr($string,$start,$length)
|
||||||
|
{
|
||||||
|
return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length);
|
||||||
|
}
|
||||||
|
}
|
107
framework/base/CStatePersister.php
Normal file
107
framework/base/CStatePersister.php
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains classes implementing security manager feature.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CStatePersister implements a file-based persistent data storage.
|
||||||
|
*
|
||||||
|
* It can be used to keep data available through multiple requests and sessions.
|
||||||
|
*
|
||||||
|
* By default, CStatePersister stores data in a file named 'state.bin' that is located
|
||||||
|
* under the application {@link CApplication::getRuntimePath runtime path}.
|
||||||
|
* You may change the location by setting the {@link stateFile} property.
|
||||||
|
*
|
||||||
|
* To retrieve the data from CStatePersister, call {@link load()}. To save the data,
|
||||||
|
* call {@link save()}.
|
||||||
|
*
|
||||||
|
* Comparison among state persister, session and cache is as follows:
|
||||||
|
* <ul>
|
||||||
|
* <li>session: data persisting within a single user session.</li>
|
||||||
|
* <li>state persister: data persisting through all requests/sessions (e.g. hit counter).</li>
|
||||||
|
* <li>cache: volatile and fast storage. It may be used as storage medium for session or state persister.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Since server resource is often limited, be cautious if you plan to use CStatePersister
|
||||||
|
* to store large amount of data. You should also consider using database-based persister
|
||||||
|
* to improve the throughput.
|
||||||
|
*
|
||||||
|
* CStatePersister is a core application component used to store global application state.
|
||||||
|
* It may be accessed via {@link CApplication::getStatePersister()}.
|
||||||
|
* page state persistent method based on cache.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CStatePersister extends CApplicationComponent implements IStatePersister
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the file path storing the state data. Make sure the directory containing
|
||||||
|
* the file exists and is writable by the Web server process. If using relative path, also
|
||||||
|
* make sure the path is correct.
|
||||||
|
*/
|
||||||
|
public $stateFile;
|
||||||
|
/**
|
||||||
|
* @var string the ID of the cache application component that is used to cache the state values.
|
||||||
|
* Defaults to 'cache' which refers to the primary cache application component.
|
||||||
|
* Set this property to false if you want to disable caching state values.
|
||||||
|
*/
|
||||||
|
public $cacheID='cache';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the component.
|
||||||
|
* This method overrides the parent implementation by making sure {@link stateFile}
|
||||||
|
* contains valid value.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if($this->stateFile===null)
|
||||||
|
$this->stateFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'state.bin';
|
||||||
|
$dir=dirname($this->stateFile);
|
||||||
|
if(!is_dir($dir) || !is_writable($dir))
|
||||||
|
throw new CException(Yii::t('yii','Unable to create application state file "{file}". Make sure the directory containing the file exists and is writable by the Web server process.',
|
||||||
|
array('{file}'=>$this->stateFile)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads state data from persistent storage.
|
||||||
|
* @return mixed state data. Null if no state data available.
|
||||||
|
*/
|
||||||
|
public function load()
|
||||||
|
{
|
||||||
|
$stateFile=$this->stateFile;
|
||||||
|
if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)
|
||||||
|
{
|
||||||
|
$cacheKey='Yii.CStatePersister.'.$stateFile;
|
||||||
|
if(($value=$cache->get($cacheKey))!==false)
|
||||||
|
return unserialize($value);
|
||||||
|
elseif(($content=@file_get_contents($stateFile))!==false)
|
||||||
|
{
|
||||||
|
$cache->set($cacheKey,$content,0,new CFileCacheDependency($stateFile));
|
||||||
|
return unserialize($content);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
elseif(($content=@file_get_contents($stateFile))!==false)
|
||||||
|
return unserialize($content);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves application state in persistent storage.
|
||||||
|
* @param mixed $state state data (must be serializable).
|
||||||
|
*/
|
||||||
|
public function save($state)
|
||||||
|
{
|
||||||
|
file_put_contents($this->stateFile,serialize($state),LOCK_EX);
|
||||||
|
}
|
||||||
|
}
|
631
framework/base/interfaces.php
Normal file
631
framework/base/interfaces.php
Normal file
@ -0,0 +1,631 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file contains core interfaces for Yii framework.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IApplicationComponent is the interface that all application components must implement.
|
||||||
|
*
|
||||||
|
* After the application completes configuration, it will invoke the {@link init()}
|
||||||
|
* method of every loaded application component.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IApplicationComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes the application component.
|
||||||
|
* This method is invoked after the application completes configuration.
|
||||||
|
*/
|
||||||
|
public function init();
|
||||||
|
/**
|
||||||
|
* @return boolean whether the {@link init()} method has been invoked.
|
||||||
|
*/
|
||||||
|
public function getIsInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICache is the interface that must be implemented by cache components.
|
||||||
|
*
|
||||||
|
* This interface must be implemented by classes supporting caching feature.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface ICache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return mixed the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
public function get($id);
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* Some caches (such as memcache, apc) allow retrieving multiple cached values at one time,
|
||||||
|
* which may improve the performance since it reduces the communication cost.
|
||||||
|
* In case a cache doesn't support this feature natively, it will be simulated by this method.
|
||||||
|
* @param array $ids list of keys identifying the cached values
|
||||||
|
* @return array list of cached values corresponding to the specified keys. The array
|
||||||
|
* is returned in terms of (key,value) pairs.
|
||||||
|
* If a value is not cached or expired, the corresponding array value will be false.
|
||||||
|
*/
|
||||||
|
public function mget($ids);
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache.
|
||||||
|
* If the cache already contains such a key, the existing value and
|
||||||
|
* expiration time will be replaced with the new ones.
|
||||||
|
*
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labelled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function set($id,$value,$expire=0,$dependency=null);
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* Nothing will be done if the cache already contains the key.
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labelled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function add($id,$value,$expire=0,$dependency=null);
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* @param string $id the key of the value to be deleted
|
||||||
|
* @return boolean whether the deletion is successful
|
||||||
|
*/
|
||||||
|
public function delete($id);
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* Be careful of performing this operation if the cache is shared by multiple applications.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
*/
|
||||||
|
public function flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ICacheDependency is the interface that must be implemented by cache dependency classes.
|
||||||
|
*
|
||||||
|
* This interface must be implemented by classes meant to be used as
|
||||||
|
* cache dependencies.
|
||||||
|
*
|
||||||
|
* Objects implementing this interface must be able to be serialized and unserialized.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface ICacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Evaluates the dependency by generating and saving the data related with dependency.
|
||||||
|
* This method is invoked by cache before writing data into it.
|
||||||
|
*/
|
||||||
|
public function evaluateDependency();
|
||||||
|
/**
|
||||||
|
* @return boolean whether the dependency has changed.
|
||||||
|
*/
|
||||||
|
public function getHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IStatePersister is the interface that must be implemented by state persister classes.
|
||||||
|
*
|
||||||
|
* This interface must be implemented by all state persister classes (such as
|
||||||
|
* {@link CStatePersister}.
|
||||||
|
*
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IStatePersister
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Loads state data from a persistent storage.
|
||||||
|
* @return mixed the state
|
||||||
|
*/
|
||||||
|
public function load();
|
||||||
|
/**
|
||||||
|
* Saves state data into a persistent storage.
|
||||||
|
* @param mixed $state the state to be saved
|
||||||
|
*/
|
||||||
|
public function save($state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IFilter is the interface that must be implemented by action filters.
|
||||||
|
*
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Performs the filtering.
|
||||||
|
* This method should be implemented to perform actual filtering.
|
||||||
|
* If the filter wants to continue the action execution, it should call
|
||||||
|
* <code>$filterChain->run()</code>.
|
||||||
|
* @param CFilterChain $filterChain the filter chain that the filter is on.
|
||||||
|
*/
|
||||||
|
public function filter($filterChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IAction is the interface that must be implemented by controller actions.
|
||||||
|
*
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string id of the action
|
||||||
|
*/
|
||||||
|
public function getId();
|
||||||
|
/**
|
||||||
|
* @return CController the controller instance
|
||||||
|
*/
|
||||||
|
public function getController();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IWebServiceProvider interface may be implemented by Web service provider classes.
|
||||||
|
*
|
||||||
|
* If this interface is implemented, the provider instance will be able
|
||||||
|
* to intercept the remote method invocation (e.g. for logging or authentication purpose).
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IWebServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method is invoked before the requested remote method is invoked.
|
||||||
|
* @param CWebService $service the currently requested Web service.
|
||||||
|
* @return boolean whether the remote method should be executed.
|
||||||
|
*/
|
||||||
|
public function beforeWebMethod($service);
|
||||||
|
/**
|
||||||
|
* This method is invoked after the requested remote method is invoked.
|
||||||
|
* @param CWebService $service the currently requested Web service.
|
||||||
|
*/
|
||||||
|
public function afterWebMethod($service);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IViewRenderer interface is implemented by a view renderer class.
|
||||||
|
*
|
||||||
|
* A view renderer is {@link CWebApplication::viewRenderer viewRenderer}
|
||||||
|
* application component whose wants to replace the default view rendering logic
|
||||||
|
* implemented in {@link CBaseController}.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IViewRenderer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Renders a view file.
|
||||||
|
* @param CBaseController $context the controller or widget who is rendering the view file.
|
||||||
|
* @param string $file the view file path
|
||||||
|
* @param mixed $data the data to be passed to the view
|
||||||
|
* @param boolean $return whether the rendering result should be returned
|
||||||
|
* @return mixed the rendering result, or null if the rendering result is not needed.
|
||||||
|
*/
|
||||||
|
public function renderFile($context,$file,$data,$return);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IUserIdentity interface is implemented by a user identity class.
|
||||||
|
*
|
||||||
|
* An identity represents a way to authenticate a user and retrieve
|
||||||
|
* information needed to uniquely identity the user. It is normally
|
||||||
|
* used with the {@link CWebApplication::user user application component}.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IUserIdentity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Authenticates the user.
|
||||||
|
* The information needed to authenticate the user
|
||||||
|
* are usually provided in the constructor.
|
||||||
|
* @return boolean whether authentication succeeds.
|
||||||
|
*/
|
||||||
|
public function authenticate();
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the identity is authenticated.
|
||||||
|
* @return boolean whether the identity is valid.
|
||||||
|
*/
|
||||||
|
public function getIsAuthenticated();
|
||||||
|
/**
|
||||||
|
* Returns a value that uniquely represents the identity.
|
||||||
|
* @return mixed a value that uniquely represents the identity (e.g. primary key value).
|
||||||
|
*/
|
||||||
|
public function getId();
|
||||||
|
/**
|
||||||
|
* Returns the display name for the identity (e.g. username).
|
||||||
|
* @return string the display name for the identity.
|
||||||
|
*/
|
||||||
|
public function getName();
|
||||||
|
/**
|
||||||
|
* Returns the additional identity information that needs to be persistent during the user session.
|
||||||
|
* @return array additional identity information that needs to be persistent during the user session (excluding {@link id}).
|
||||||
|
*/
|
||||||
|
public function getPersistentStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IWebUser interface is implemented by a {@link CWebApplication::user user application component}.
|
||||||
|
*
|
||||||
|
* A user application component represents the identity information
|
||||||
|
* for the current user.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IWebUser
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns a value that uniquely represents the identity.
|
||||||
|
* @return mixed a value that uniquely represents the identity (e.g. primary key value).
|
||||||
|
*/
|
||||||
|
public function getId();
|
||||||
|
/**
|
||||||
|
* Returns the display name for the identity (e.g. username).
|
||||||
|
* @return string the display name for the identity.
|
||||||
|
*/
|
||||||
|
public function getName();
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the user is a guest (not authenticated).
|
||||||
|
* @return boolean whether the user is a guest (not authenticated)
|
||||||
|
*/
|
||||||
|
public function getIsGuest();
|
||||||
|
/**
|
||||||
|
* Performs access check for this user.
|
||||||
|
* @param string $operation the name of the operation that need access check.
|
||||||
|
* @param array $params name-value pairs that would be passed to business rules associated
|
||||||
|
* with the tasks and roles assigned to the user.
|
||||||
|
* @return boolean whether the operations can be performed by this user.
|
||||||
|
*/
|
||||||
|
public function checkAccess($operation,$params=array());
|
||||||
|
/**
|
||||||
|
* Redirects the user browser to the login page.
|
||||||
|
* Before the redirection, the current URL (if it's not an AJAX url) will be
|
||||||
|
* kept in {@link returnUrl} so that the user browser may be redirected back
|
||||||
|
* to the current page after successful login. Make sure you set {@link loginUrl}
|
||||||
|
* so that the user browser can be redirected to the specified login URL after
|
||||||
|
* calling this method.
|
||||||
|
* After calling this method, the current request processing will be terminated.
|
||||||
|
*/
|
||||||
|
public function loginRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IAuthManager interface is implemented by an auth manager application component.
|
||||||
|
*
|
||||||
|
* An auth manager is mainly responsible for providing role-based access control (RBAC) service.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
interface IAuthManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Performs access check for the specified user.
|
||||||
|
* @param string $itemName the name of the operation that we are checking access to
|
||||||
|
* @param mixed $userId the user ID. This should be either an integer or a string representing
|
||||||
|
* the unique identifier of a user. See {@link IWebUser::getId}.
|
||||||
|
* @param array $params name-value pairs that would be passed to biz rules associated
|
||||||
|
* with the tasks and roles assigned to the user.
|
||||||
|
* @return boolean whether the operations can be performed by the user.
|
||||||
|
*/
|
||||||
|
public function checkAccess($itemName,$userId,$params=array());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an authorization item.
|
||||||
|
* An authorization item represents an action permission (e.g. creating a post).
|
||||||
|
* It has three types: operation, task and role.
|
||||||
|
* Authorization items form a hierarchy. Higher level items inheirt permissions representing
|
||||||
|
* by lower level items.
|
||||||
|
* @param string $name the item name. This must be a unique identifier.
|
||||||
|
* @param integer $type the item type (0: operation, 1: task, 2: role).
|
||||||
|
* @param string $description description of the item
|
||||||
|
* @param string $bizRule business rule associated with the item. This is a piece of
|
||||||
|
* PHP code that will be executed when {@link checkAccess} is called for the item.
|
||||||
|
* @param mixed $data additional data associated with the item.
|
||||||
|
* @return CAuthItem the authorization item
|
||||||
|
* @throws CException if an item with the same name already exists
|
||||||
|
*/
|
||||||
|
public function createAuthItem($name,$type,$description='',$bizRule=null,$data=null);
|
||||||
|
/**
|
||||||
|
* Removes the specified authorization item.
|
||||||
|
* @param string $name the name of the item to be removed
|
||||||
|
* @return boolean whether the item exists in the storage and has been removed
|
||||||
|
*/
|
||||||
|
public function removeAuthItem($name);
|
||||||
|
/**
|
||||||
|
* Returns the authorization items of the specific type and user.
|
||||||
|
* @param integer $type the item type (0: operation, 1: task, 2: role). Defaults to null,
|
||||||
|
* meaning returning all items regardless of their type.
|
||||||
|
* @param mixed $userId the user ID. Defaults to null, meaning returning all items even if
|
||||||
|
* they are not assigned to a user.
|
||||||
|
* @return array the authorization items of the specific type.
|
||||||
|
*/
|
||||||
|
public function getAuthItems($type=null,$userId=null);
|
||||||
|
/**
|
||||||
|
* Returns the authorization item with the specified name.
|
||||||
|
* @param string $name the name of the item
|
||||||
|
* @return CAuthItem the authorization item. Null if the item cannot be found.
|
||||||
|
*/
|
||||||
|
public function getAuthItem($name);
|
||||||
|
/**
|
||||||
|
* Saves an authorization item to persistent storage.
|
||||||
|
* @param CAuthItem $item the item to be saved.
|
||||||
|
* @param string $oldName the old item name. If null, it means the item name is not changed.
|
||||||
|
*/
|
||||||
|
public function saveAuthItem($item,$oldName=null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an item as a child of another item.
|
||||||
|
* @param string $itemName the parent item name
|
||||||
|
* @param string $childName the child item name
|
||||||
|
* @throws CException if either parent or child doesn't exist or if a loop has been detected.
|
||||||
|
*/
|
||||||
|
public function addItemChild($itemName,$childName);
|
||||||
|
/**
|
||||||
|
* Removes a child from its parent.
|
||||||
|
* Note, the child item is not deleted. Only the parent-child relationship is removed.
|
||||||
|
* @param string $itemName the parent item name
|
||||||
|
* @param string $childName the child item name
|
||||||
|
* @return boolean whether the removal is successful
|
||||||
|
*/
|
||||||
|
public function removeItemChild($itemName,$childName);
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether a child exists within a parent.
|
||||||
|
* @param string $itemName the parent item name
|
||||||
|
* @param string $childName the child item name
|
||||||
|
* @return boolean whether the child exists
|
||||||
|
*/
|
||||||
|
public function hasItemChild($itemName,$childName);
|
||||||
|
/**
|
||||||
|
* Returns the children of the specified item.
|
||||||
|
* @param mixed $itemName the parent item name. This can be either a string or an array.
|
||||||
|
* The latter represents a list of item names.
|
||||||
|
* @return array all child items of the parent
|
||||||
|
*/
|
||||||
|
public function getItemChildren($itemName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns an authorization item to a user.
|
||||||
|
* @param string $itemName the item name
|
||||||
|
* @param mixed $userId the user ID (see {@link IWebUser::getId})
|
||||||
|
* @param string $bizRule the business rule to be executed when {@link checkAccess} is called
|
||||||
|
* for this particular authorization item.
|
||||||
|
* @param mixed $data additional data associated with this assignment
|
||||||
|
* @return CAuthAssignment the authorization assignment information.
|
||||||
|
* @throws CException if the item does not exist or if the item has already been assigned to the user
|
||||||
|
*/
|
||||||
|
public function assign($itemName,$userId,$bizRule=null,$data=null);
|
||||||
|
/**
|
||||||
|
* Revokes an authorization assignment from a user.
|
||||||
|
* @param string $itemName the item name
|
||||||
|
* @param mixed $userId the user ID (see {@link IWebUser::getId})
|
||||||
|
* @return boolean whether removal is successful
|
||||||
|
*/
|
||||||
|
public function revoke($itemName,$userId);
|
||||||
|
/**
|
||||||
|
* Returns a value indicating whether the item has been assigned to the user.
|
||||||
|
* @param string $itemName the item name
|
||||||
|
* @param mixed $userId the user ID (see {@link IWebUser::getId})
|
||||||
|
* @return boolean whether the item has been assigned to the user.
|
||||||
|
*/
|
||||||
|
public function isAssigned($itemName,$userId);
|
||||||
|
/**
|
||||||
|
* Returns the item assignment information.
|
||||||
|
* @param string $itemName the item name
|
||||||
|
* @param mixed $userId the user ID (see {@link IWebUser::getId})
|
||||||
|
* @return CAuthAssignment the item assignment information. Null is returned if
|
||||||
|
* the item is not assigned to the user.
|
||||||
|
*/
|
||||||
|
public function getAuthAssignment($itemName,$userId);
|
||||||
|
/**
|
||||||
|
* Returns the item assignments for the specified user.
|
||||||
|
* @param mixed $userId the user ID (see {@link IWebUser::getId})
|
||||||
|
* @return array the item assignment information for the user. An empty array will be
|
||||||
|
* returned if there is no item assigned to the user.
|
||||||
|
*/
|
||||||
|
public function getAuthAssignments($userId);
|
||||||
|
/**
|
||||||
|
* Saves the changes to an authorization assignment.
|
||||||
|
* @param CAuthAssignment $assignment the assignment that has been changed.
|
||||||
|
*/
|
||||||
|
public function saveAuthAssignment($assignment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all authorization data.
|
||||||
|
*/
|
||||||
|
public function clearAll();
|
||||||
|
/**
|
||||||
|
* Removes all authorization assignments.
|
||||||
|
*/
|
||||||
|
public function clearAuthAssignments();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves authorization data into persistent storage.
|
||||||
|
* If any change is made to the authorization data, please make
|
||||||
|
* sure you call this method to save the changed data into persistent storage.
|
||||||
|
*/
|
||||||
|
public function save();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a business rule.
|
||||||
|
* A business rule is a piece of PHP code that will be executed when {@link checkAccess} is called.
|
||||||
|
* @param string $bizRule the business rule to be executed.
|
||||||
|
* @param array $params additional parameters to be passed to the business rule when being executed.
|
||||||
|
* @param mixed $data additional data that is associated with the corresponding authorization item or assignment
|
||||||
|
* @return boolean whether the execution returns a true value.
|
||||||
|
* If the business rule is empty, it will also return true.
|
||||||
|
*/
|
||||||
|
public function executeBizRule($bizRule,$params,$data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IBehavior interfaces is implemented by all behavior classes.
|
||||||
|
*
|
||||||
|
* A behavior is a way to enhance a component with additional methods that
|
||||||
|
* are defined in the behavior class and not available in the component class.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.base
|
||||||
|
*/
|
||||||
|
interface IBehavior
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Attaches the behavior object to the component.
|
||||||
|
* @param CComponent $component the component that this behavior is to be attached to.
|
||||||
|
*/
|
||||||
|
public function attach($component);
|
||||||
|
/**
|
||||||
|
* Detaches the behavior object from the component.
|
||||||
|
* @param CComponent $component the component that this behavior is to be detached from.
|
||||||
|
*/
|
||||||
|
public function detach($component);
|
||||||
|
/**
|
||||||
|
* @return boolean whether this behavior is enabled
|
||||||
|
*/
|
||||||
|
public function getEnabled();
|
||||||
|
/**
|
||||||
|
* @param boolean $value whether this behavior is enabled
|
||||||
|
*/
|
||||||
|
public function setEnabled($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IWidgetFactory is the interface that must be implemented by a widget factory class.
|
||||||
|
*
|
||||||
|
* When calling {@link CBaseController::createWidget}, if a widget factory is available,
|
||||||
|
* it will be used for creating the requested widget.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.web
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
interface IWidgetFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new widget based on the given class name and initial properties.
|
||||||
|
* @param CBaseController $owner the owner of the new widget
|
||||||
|
* @param string $className the class name of the widget. This can also be a path alias (e.g. system.web.widgets.COutputCache)
|
||||||
|
* @param array $properties the initial property values (name=>value) of the widget.
|
||||||
|
* @return CWidget the newly created widget whose properties have been initialized with the given values.
|
||||||
|
*/
|
||||||
|
public function createWidget($owner,$className,$properties=array());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IDataProvider is the interface that must be implemented by data provider classes.
|
||||||
|
*
|
||||||
|
* Data providers are components that can feed data for widgets such as data grid, data list.
|
||||||
|
* Besides providing data, they also support pagination and sorting.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.web
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
interface IDataProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string the unique ID that identifies the data provider from other data providers.
|
||||||
|
*/
|
||||||
|
public function getId();
|
||||||
|
/**
|
||||||
|
* Returns the number of data items in the current page.
|
||||||
|
* This is equivalent to <code>count($provider->getData())</code>.
|
||||||
|
* When {@link pagination} is set false, this returns the same value as {@link totalItemCount}.
|
||||||
|
* @param boolean $refresh whether the number of data items should be re-calculated.
|
||||||
|
* @return integer the number of data items in the current page.
|
||||||
|
*/
|
||||||
|
public function getItemCount($refresh=false);
|
||||||
|
/**
|
||||||
|
* Returns the total number of data items.
|
||||||
|
* When {@link pagination} is set false, this returns the same value as {@link itemCount}.
|
||||||
|
* @param boolean $refresh whether the total number of data items should be re-calculated.
|
||||||
|
* @return integer total number of possible data items.
|
||||||
|
*/
|
||||||
|
public function getTotalItemCount($refresh=false);
|
||||||
|
/**
|
||||||
|
* Returns the data items currently available.
|
||||||
|
* @param boolean $refresh whether the data should be re-fetched from persistent storage.
|
||||||
|
* @return array the list of data items currently available in this data provider.
|
||||||
|
*/
|
||||||
|
public function getData($refresh=false);
|
||||||
|
/**
|
||||||
|
* Returns the key values associated with the data items.
|
||||||
|
* @param boolean $refresh whether the keys should be re-calculated.
|
||||||
|
* @return array the list of key values corresponding to {@link data}. Each data item in {@link data}
|
||||||
|
* is uniquely identified by the corresponding key value in this array.
|
||||||
|
*/
|
||||||
|
public function getKeys($refresh=false);
|
||||||
|
/**
|
||||||
|
* @return CSort the sorting object. If this is false, it means the sorting is disabled.
|
||||||
|
*/
|
||||||
|
public function getSort();
|
||||||
|
/**
|
||||||
|
* @return CPagination the pagination object. If this is false, it means the pagination is disabled.
|
||||||
|
*/
|
||||||
|
public function getPagination();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ILogFilter is the interface that must be implemented by log filters.
|
||||||
|
*
|
||||||
|
* A log filter preprocesses the logged messages before they are handled by a log route.
|
||||||
|
* You can attach classes that implement ILogFilter to {@link CLogRoute::$filter}.
|
||||||
|
*
|
||||||
|
* @package system.logging
|
||||||
|
* @since 1.1.11
|
||||||
|
*/
|
||||||
|
interface ILogFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This method should be implemented to perform actual filtering of log messages
|
||||||
|
* by working on the array given as the first parameter.
|
||||||
|
* Implementation might reformat, remove or add information to logged messages.
|
||||||
|
* @param array $logs list of messages. Each array element represents one message
|
||||||
|
* with the following structure:
|
||||||
|
* array(
|
||||||
|
* [0] => message (string)
|
||||||
|
* [1] => level (string)
|
||||||
|
* [2] => category (string)
|
||||||
|
* [3] => timestamp (float, obtained by microtime(true));
|
||||||
|
*/
|
||||||
|
public function filter(&$logs);
|
||||||
|
}
|
||||||
|
|
108
framework/caching/CApcCache.php
Normal file
108
framework/caching/CApcCache.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CApcCache class file
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CApcCache provides APC caching in terms of an application component.
|
||||||
|
*
|
||||||
|
* The caching is based on {@link http://www.php.net/apc APC}.
|
||||||
|
* To use this application component, the APC PHP extension must be loaded.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CApcCache.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CApcCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It checks the availability of APC.
|
||||||
|
* @throws CException if APC cache extension is not loaded or is disabled.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if(!extension_loaded('apc'))
|
||||||
|
throw new CException(Yii::t('yii','CApcCache requires PHP apc extension to be loaded.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
return apc_fetch($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
return apc_fetch($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return apc_store($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return apc_add($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return apc_delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
return apc_clear_cache('user');
|
||||||
|
}
|
||||||
|
}
|
376
framework/caching/CCache.php
Normal file
376
framework/caching/CCache.php
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CCache class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CCache is the base class for cache classes with different cache storage implementation.
|
||||||
|
*
|
||||||
|
* A data item can be stored in cache by calling {@link set} and be retrieved back
|
||||||
|
* later by {@link get}. In both operations, a key identifying the data item is required.
|
||||||
|
* An expiration time and/or a dependency can also be specified when calling {@link set}.
|
||||||
|
* If the data item expires or the dependency changes, calling {@link get} will not
|
||||||
|
* return back the data item.
|
||||||
|
*
|
||||||
|
* Note, by definition, cache does not ensure the existence of a value
|
||||||
|
* even if it does not expire. Cache is not meant to be a persistent storage.
|
||||||
|
*
|
||||||
|
* CCache implements the interface {@link ICache} with the following methods:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link get} : retrieve the value with a key (if any) from cache</li>
|
||||||
|
* <li>{@link set} : store the value with a key into cache</li>
|
||||||
|
* <li>{@link add} : store the value only if cache does not have this key</li>
|
||||||
|
* <li>{@link delete} : delete the value with the specified key from cache</li>
|
||||||
|
* <li>{@link flush} : delete all values from cache</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Child classes must implement the following methods:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link getValue}</li>
|
||||||
|
* <li>{@link setValue}</li>
|
||||||
|
* <li>{@link addValue}</li>
|
||||||
|
* <li>{@link deleteValue}</li>
|
||||||
|
* <li>{@link getValues} (optional)</li>
|
||||||
|
* <li>{@link flushValues} (optional)</li>
|
||||||
|
* <li>{@link serializer} (optional)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* CCache also implements ArrayAccess so that it can be used like an array.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
abstract class CCache extends CApplicationComponent implements ICache, ArrayAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string a string prefixed to every cache key so that it is unique. Defaults to null which means
|
||||||
|
* to use the {@link CApplication::getId() application ID}. If different applications need to access the same
|
||||||
|
* pool of cached data, the same prefix should be set for each of the applications explicitly.
|
||||||
|
*/
|
||||||
|
public $keyPrefix;
|
||||||
|
/**
|
||||||
|
* @var boolean whether to md5-hash the cache key for normalization purposes. Defaults to true. Setting this property to false makes sure the cache
|
||||||
|
* key will not be tampered when calling the relevant methods {@link get()}, {@link set()}, {@link add()} and {@link delete()}. This is useful if a Yii
|
||||||
|
* application as well as an external application need to access the same cache pool (also see description of {@link keyPrefix} regarding this use case).
|
||||||
|
* However, without normalization you should make sure the affected cache backend does support the structure (charset, length, etc.) of all the provided
|
||||||
|
* cache keys, otherwise there might be unexpected behavior.
|
||||||
|
* @since 1.1.11
|
||||||
|
**/
|
||||||
|
public $hashKey=true;
|
||||||
|
/**
|
||||||
|
* @var array|boolean the functions used to serialize and unserialize cached data. Defaults to null, meaning
|
||||||
|
* using the default PHP `serialize()` and `unserialize()` functions. If you want to use some more efficient
|
||||||
|
* serializer (e.g. {@link http://pecl.php.net/package/igbinary igbinary}), you may configure this property with
|
||||||
|
* a two-element array. The first element specifies the serialization function, and the second the deserialization
|
||||||
|
* function. If this property is set false, data will be directly sent to and retrieved from the underlying
|
||||||
|
* cache component without any serialization or deserialization. You should not turn off serialization if
|
||||||
|
* you are using {@link CCacheDependency cache dependency}, because it relies on data serialization.
|
||||||
|
*/
|
||||||
|
public $serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the application component.
|
||||||
|
* This method overrides the parent implementation by setting default cache key prefix.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if($this->keyPrefix===null)
|
||||||
|
$this->keyPrefix=Yii::app()->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key a key identifying a value to be cached
|
||||||
|
* @return string a key generated from the provided key which ensures the uniqueness across applications
|
||||||
|
*/
|
||||||
|
protected function generateUniqueKey($key)
|
||||||
|
{
|
||||||
|
return $this->hashKey ? md5($this->keyPrefix.$key) : $this->keyPrefix.$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed.
|
||||||
|
*/
|
||||||
|
public function get($id)
|
||||||
|
{
|
||||||
|
$value = $this->getValue($this->generateUniqueKey($id));
|
||||||
|
if($value===false || $this->serializer===false)
|
||||||
|
return $value;
|
||||||
|
if($this->serializer===null)
|
||||||
|
$value=unserialize($value);
|
||||||
|
else
|
||||||
|
$value=call_user_func($this->serializer[1], $value);
|
||||||
|
if(is_array($value) && (!$value[1] instanceof ICacheDependency || !$value[1]->getHasChanged()))
|
||||||
|
{
|
||||||
|
Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this));
|
||||||
|
return $value[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* Some caches (such as memcache, apc) allow retrieving multiple cached values at one time,
|
||||||
|
* which may improve the performance since it reduces the communication cost.
|
||||||
|
* In case a cache does not support this feature natively, it will be simulated by this method.
|
||||||
|
* @param array $ids list of keys identifying the cached values
|
||||||
|
* @return array list of cached values corresponding to the specified keys. The array
|
||||||
|
* is returned in terms of (key,value) pairs.
|
||||||
|
* If a value is not cached or expired, the corresponding array value will be false.
|
||||||
|
*/
|
||||||
|
public function mget($ids)
|
||||||
|
{
|
||||||
|
$uids = array();
|
||||||
|
foreach ($ids as $id)
|
||||||
|
$uids[$id] = $this->generateUniqueKey($id);
|
||||||
|
|
||||||
|
$values = $this->getValues($uids);
|
||||||
|
$results = array();
|
||||||
|
if($this->serializer === false)
|
||||||
|
{
|
||||||
|
foreach ($uids as $id => $uid)
|
||||||
|
$results[$id] = isset($values[$uid]) ? $values[$uid] : false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($uids as $id => $uid)
|
||||||
|
{
|
||||||
|
$results[$id] = false;
|
||||||
|
if(isset($values[$uid]))
|
||||||
|
{
|
||||||
|
$value = $this->serializer === null ? unserialize($values[$uid]) : call_user_func($this->serializer[1], $values[$uid]);
|
||||||
|
if(is_array($value) && (!$value[1] instanceof ICacheDependency || !$value[1]->getHasChanged()))
|
||||||
|
{
|
||||||
|
Yii::trace('Serving "'.$id.'" from cache','system.caching.'.get_class($this));
|
||||||
|
$results[$id] = $value[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache.
|
||||||
|
* If the cache already contains such a key, the existing value and
|
||||||
|
* expiration time will be replaced with the new ones.
|
||||||
|
*
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function set($id,$value,$expire=0,$dependency=null)
|
||||||
|
{
|
||||||
|
Yii::trace('Saving "'.$id.'" to cache','system.caching.'.get_class($this));
|
||||||
|
|
||||||
|
if ($dependency !== null && $this->serializer !== false)
|
||||||
|
$dependency->evaluateDependency();
|
||||||
|
|
||||||
|
if ($this->serializer === null)
|
||||||
|
$value = serialize(array($value,$dependency));
|
||||||
|
elseif ($this->serializer !== false)
|
||||||
|
$value = call_user_func($this->serializer[0], array($value,$dependency));
|
||||||
|
|
||||||
|
return $this->setValue($this->generateUniqueKey($id), $value, $expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* Nothing will be done if the cache already contains the key.
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function add($id,$value,$expire=0,$dependency=null)
|
||||||
|
{
|
||||||
|
Yii::trace('Adding "'.$id.'" to cache','system.caching.'.get_class($this));
|
||||||
|
|
||||||
|
if ($dependency !== null && $this->serializer !== false)
|
||||||
|
$dependency->evaluateDependency();
|
||||||
|
|
||||||
|
if ($this->serializer === null)
|
||||||
|
$value = serialize(array($value,$dependency));
|
||||||
|
elseif ($this->serializer !== false)
|
||||||
|
$value = call_user_func($this->serializer[0], array($value,$dependency));
|
||||||
|
|
||||||
|
return $this->addValue($this->generateUniqueKey($id), $value, $expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* @param string $id the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
Yii::trace('Deleting "'.$id.'" from cache','system.caching.'.get_class($this));
|
||||||
|
return $this->deleteValue($this->generateUniqueKey($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* Be careful of performing this operation if the cache is shared by multiple applications.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
*/
|
||||||
|
public function flush()
|
||||||
|
{
|
||||||
|
Yii::trace('Flushing cache','system.caching.'.get_class($this));
|
||||||
|
return $this->flushValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This method should be implemented by child classes to retrieve the data
|
||||||
|
* from specific cache storage. The uniqueness and dependency are handled
|
||||||
|
* in {@link get()} already. So only the implementation of data retrieval
|
||||||
|
* is needed.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','{className} does not support get() functionality.',
|
||||||
|
array('{className}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* The default implementation simply calls {@link getValue} multiple
|
||||||
|
* times to retrieve the cached values one by one.
|
||||||
|
* If the underlying cache storage supports multiget, this method should
|
||||||
|
* be overridden to exploit that feature.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
$results=array();
|
||||||
|
foreach($keys as $key)
|
||||||
|
$results[$key]=$this->getValue($key);
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This method should be implemented by child classes to store the data
|
||||||
|
* in specific cache storage. The uniqueness and dependency are handled
|
||||||
|
* in {@link set()} already. So only the implementation of data storage
|
||||||
|
* is needed.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','{className} does not support set() functionality.',
|
||||||
|
array('{className}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This method should be implemented by child classes to store the data
|
||||||
|
* in specific cache storage. The uniqueness and dependency are handled
|
||||||
|
* in {@link add()} already. So only the implementation of data storage
|
||||||
|
* is needed.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','{className} does not support add() functionality.',
|
||||||
|
array('{className}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This method should be implemented by child classes to delete the data from actual cache storage.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','{className} does not support delete() functionality.',
|
||||||
|
array('{className}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* Child classes may implement this method to realize the flush operation.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
throw new CException(Yii::t('yii','{className} does not support flushValues() functionality.',
|
||||||
|
array('{className}'=>get_class($this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is a cache entry with a specified key.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function offsetExists($id)
|
||||||
|
{
|
||||||
|
return $this->get($id)!==false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value from cache with a specified key.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return mixed the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
public function offsetGet($id)
|
||||||
|
{
|
||||||
|
return $this->get($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the value identified by a key into cache.
|
||||||
|
* If the cache already contains such a key, the existing value will be
|
||||||
|
* replaced with the new ones. To add expiration and dependencies, use the set() method.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
*/
|
||||||
|
public function offsetSet($id, $value)
|
||||||
|
{
|
||||||
|
$this->set($id, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the value with the specified key from cache
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
public function offsetUnset($id)
|
||||||
|
{
|
||||||
|
$this->delete($id);
|
||||||
|
}
|
||||||
|
}
|
313
framework/caching/CDbCache.php
Normal file
313
framework/caching/CDbCache.php
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CDbCache class file
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDbCache implements a cache application component by storing cached data in a database.
|
||||||
|
*
|
||||||
|
* CDbCache stores cache data in a DB table named {@link cacheTableName}.
|
||||||
|
* If the table does not exist, it will be automatically created.
|
||||||
|
* By setting {@link autoCreateCacheTable} to false, you can also manually create the DB table.
|
||||||
|
*
|
||||||
|
* CDbCache relies on {@link http://www.php.net/manual/en/ref.pdo.php PDO} to access database.
|
||||||
|
* By default, it will use a SQLite3 database under the application runtime directory.
|
||||||
|
* You can also specify {@link connectionID} so that it makes use of
|
||||||
|
* a DB application component to access database.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CDbCache.
|
||||||
|
*
|
||||||
|
* @property integer $gCProbability The probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
* @property CDbConnection $dbConnection The DB connection instance.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CDbCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the ID of the {@link CDbConnection} application component. If not set,
|
||||||
|
* a SQLite3 database will be automatically created and used. The SQLite database file
|
||||||
|
* is <code>protected/runtime/cache-YiiVersion.db</code>.
|
||||||
|
*/
|
||||||
|
public $connectionID;
|
||||||
|
/**
|
||||||
|
* @var string name of the DB table to store cache content. Defaults to 'YiiCache'.
|
||||||
|
* Note, if {@link autoCreateCacheTable} is false and you want to create the DB table
|
||||||
|
* manually by yourself, you need to make sure the DB table is of the following structure:
|
||||||
|
* <pre>
|
||||||
|
* (id CHAR(128) PRIMARY KEY, expire INTEGER, value BLOB)
|
||||||
|
* </pre>
|
||||||
|
* Note, some DBMS might not support BLOB type. In this case, replace 'BLOB' with a suitable
|
||||||
|
* binary data type (e.g. LONGBLOB in MySQL, BYTEA in PostgreSQL.)
|
||||||
|
* @see autoCreateCacheTable
|
||||||
|
*/
|
||||||
|
public $cacheTableName='YiiCache';
|
||||||
|
/**
|
||||||
|
* @var boolean whether the cache DB table should be created automatically if it does not exist. Defaults to true.
|
||||||
|
* If you already have the table created, it is recommended you set this property to be false to improve performance.
|
||||||
|
* @see cacheTableName
|
||||||
|
*/
|
||||||
|
public $autoCreateCacheTable=true;
|
||||||
|
/**
|
||||||
|
* @var CDbConnection the DB connection instance
|
||||||
|
*/
|
||||||
|
private $_db;
|
||||||
|
private $_gcProbability=100;
|
||||||
|
private $_gced=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
*
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It ensures the existence of the cache DB table.
|
||||||
|
* It also removes expired data items from the cache.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
$db->setActive(true);
|
||||||
|
if($this->autoCreateCacheTable)
|
||||||
|
{
|
||||||
|
$sql="DELETE FROM {$this->cacheTableName} WHERE expire>0 AND expire<".time();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$db->createCommand($sql)->execute();
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
$this->createCacheTable($db,$this->cacheTableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return integer the probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
*/
|
||||||
|
public function getGCProbability()
|
||||||
|
{
|
||||||
|
return $this->_gcProbability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param integer $value the probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
* This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all.
|
||||||
|
*/
|
||||||
|
public function setGCProbability($value)
|
||||||
|
{
|
||||||
|
$value=(int)$value;
|
||||||
|
if($value<0)
|
||||||
|
$value=0;
|
||||||
|
if($value>1000000)
|
||||||
|
$value=1000000;
|
||||||
|
$this->_gcProbability=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the cache DB table.
|
||||||
|
* @param CDbConnection $db the database connection
|
||||||
|
* @param string $tableName the name of the table to be created
|
||||||
|
*/
|
||||||
|
protected function createCacheTable($db,$tableName)
|
||||||
|
{
|
||||||
|
$driver=$db->getDriverName();
|
||||||
|
if($driver==='mysql')
|
||||||
|
$blob='LONGBLOB';
|
||||||
|
elseif($driver==='pgsql')
|
||||||
|
$blob='BYTEA';
|
||||||
|
else
|
||||||
|
$blob='BLOB';
|
||||||
|
$sql=<<<EOD
|
||||||
|
CREATE TABLE $tableName
|
||||||
|
(
|
||||||
|
id CHAR(128) PRIMARY KEY,
|
||||||
|
expire INTEGER,
|
||||||
|
value $blob
|
||||||
|
)
|
||||||
|
EOD;
|
||||||
|
$db->createCommand($sql)->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CDbConnection the DB connection instance
|
||||||
|
* @throws CException if {@link connectionID} does not point to a valid application component.
|
||||||
|
*/
|
||||||
|
public function getDbConnection()
|
||||||
|
{
|
||||||
|
if($this->_db!==null)
|
||||||
|
return $this->_db;
|
||||||
|
elseif(($id=$this->connectionID)!==null)
|
||||||
|
{
|
||||||
|
if(($this->_db=Yii::app()->getComponent($id)) instanceof CDbConnection)
|
||||||
|
return $this->_db;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CDbCache.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.',
|
||||||
|
array('{id}'=>$id)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$dbFile=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache-'.Yii::getVersion().'.db';
|
||||||
|
return $this->_db=new CDbConnection('sqlite:'.$dbFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the DB connection used by the cache component.
|
||||||
|
* @param CDbConnection $value the DB connection instance
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
public function setDbConnection($value)
|
||||||
|
{
|
||||||
|
$this->_db=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
$time=time();
|
||||||
|
$sql="SELECT value FROM {$this->cacheTableName} WHERE id='$key' AND (expire=0 OR expire>$time)";
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
if($db->queryCachingDuration>0)
|
||||||
|
{
|
||||||
|
$duration=$db->queryCachingDuration;
|
||||||
|
$db->queryCachingDuration=0;
|
||||||
|
$result=$db->createCommand($sql)->queryScalar();
|
||||||
|
$db->queryCachingDuration=$duration;
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $db->createCommand($sql)->queryScalar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
if(empty($keys))
|
||||||
|
return array();
|
||||||
|
|
||||||
|
$ids=implode("','",$keys);
|
||||||
|
$time=time();
|
||||||
|
$sql="SELECT id, value FROM {$this->cacheTableName} WHERE id IN ('$ids') AND (expire=0 OR expire>$time)";
|
||||||
|
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
if($db->queryCachingDuration>0)
|
||||||
|
{
|
||||||
|
$duration=$db->queryCachingDuration;
|
||||||
|
$db->queryCachingDuration=0;
|
||||||
|
$rows=$db->createCommand($sql)->queryAll();
|
||||||
|
$db->queryCachingDuration=$duration;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$rows=$db->createCommand($sql)->queryAll();
|
||||||
|
|
||||||
|
$results=array();
|
||||||
|
foreach($keys as $key)
|
||||||
|
$results[$key]=false;
|
||||||
|
foreach($rows as $row)
|
||||||
|
$results[$row['id']]=$row['value'];
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
$this->deleteValue($key);
|
||||||
|
return $this->addValue($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability)
|
||||||
|
{
|
||||||
|
$this->gc();
|
||||||
|
$this->_gced=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($expire>0)
|
||||||
|
$expire+=time();
|
||||||
|
else
|
||||||
|
$expire=0;
|
||||||
|
$sql="INSERT INTO {$this->cacheTableName} (id,expire,value) VALUES ('$key',$expire,:value)";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$command=$this->getDbConnection()->createCommand($sql);
|
||||||
|
$command->bindValue(':value',$value,PDO::PARAM_LOB);
|
||||||
|
$command->execute();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
$sql="DELETE FROM {$this->cacheTableName} WHERE id='$key'";
|
||||||
|
$this->getDbConnection()->createCommand($sql)->execute();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the expired data values.
|
||||||
|
*/
|
||||||
|
protected function gc()
|
||||||
|
{
|
||||||
|
$this->getDbConnection()->createCommand("DELETE FROM {$this->cacheTableName} WHERE expire>0 AND expire<".time())->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
$this->getDbConnection()->createCommand("DELETE FROM {$this->cacheTableName}")->execute();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
163
framework/caching/CDummyCache.php
Normal file
163
framework/caching/CDummyCache.php
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CDummyCache class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDummyCache is a placeholder cache component.
|
||||||
|
*
|
||||||
|
* CDummyCache does not cache anything. It is provided so that one can always configure
|
||||||
|
* a 'cache' application component and he does not need to check if Yii::app()->cache is null or not.
|
||||||
|
* By replacing CDummyCache with some other cache component, one can quickly switch from
|
||||||
|
* non-caching mode to caching mode.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CDummyCache extends CApplicationComponent implements ICache, ArrayAccess
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string a string prefixed to every cache key so that it is unique. Defaults to {@link CApplication::getId() application ID}.
|
||||||
|
*/
|
||||||
|
public $keyPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the application component.
|
||||||
|
* This method overrides the parent implementation by setting default cache key prefix.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if($this->keyPrefix===null)
|
||||||
|
$this->keyPrefix=Yii::app()->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return mixed the value stored in cache, false if the value is not in the cache, expired or the dependency has changed.
|
||||||
|
*/
|
||||||
|
public function get($id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* Some caches (such as memcache, apc) allow retrieving multiple cached values at one time,
|
||||||
|
* which may improve the performance since it reduces the communication cost.
|
||||||
|
* In case a cache doesn't support this feature natively, it will be simulated by this method.
|
||||||
|
* @param array $ids list of keys identifying the cached values
|
||||||
|
* @return array list of cached values corresponding to the specified keys. The array
|
||||||
|
* is returned in terms of (key,value) pairs.
|
||||||
|
* If a value is not cached or expired, the corresponding array value will be false.
|
||||||
|
*/
|
||||||
|
public function mget($ids)
|
||||||
|
{
|
||||||
|
$results=array();
|
||||||
|
foreach($ids as $id)
|
||||||
|
$results[$id]=false;
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache.
|
||||||
|
* If the cache already contains such a key, the existing value and
|
||||||
|
* expiration time will be replaced with the new ones.
|
||||||
|
*
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function set($id,$value,$expire=0,$dependency=null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* Nothing will be done if the cache already contains the key.
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @param ICacheDependency $dependency dependency of the cached item. If the dependency changes, the item is labeled invalid.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
public function add($id,$value,$expire=0,$dependency=null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* @param string $id the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* Be careful of performing this operation if the cache is shared by multiple applications.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @throws CException if this method is not overridden by child classes
|
||||||
|
*/
|
||||||
|
public function flush()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there is a cache entry with a specified key.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function offsetExists($id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the value from cache with a specified key.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id a key identifying the cached value
|
||||||
|
* @return mixed the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
public function offsetGet($id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the value identified by a key into cache.
|
||||||
|
* If the cache already contains such a key, the existing value will be
|
||||||
|
* replaced with the new ones. To add expiration and dependencies, use the set() method.
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id the key identifying the value to be cached
|
||||||
|
* @param mixed $value the value to be cached
|
||||||
|
*/
|
||||||
|
public function offsetSet($id, $value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the value with the specified key from cache
|
||||||
|
* This method is required by the interface ArrayAccess.
|
||||||
|
* @param string $id the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
public function offsetUnset($id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
106
framework/caching/CEAcceleratorCache.php
Normal file
106
framework/caching/CEAcceleratorCache.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CEAcceleratorCache class file
|
||||||
|
*
|
||||||
|
* @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CEAcceleratorCache implements a cache application module based on {@link http://eaccelerator.net/ eaccelerator}.
|
||||||
|
*
|
||||||
|
* To use this application component, the eAccelerator PHP extension must be loaded.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CEAccelerator.
|
||||||
|
*
|
||||||
|
* Please note that as of v0.9.6, eAccelerator no longer supports data caching.
|
||||||
|
* This means if you still want to use this component, your eAccelerator should be of 0.9.5.x or lower version.
|
||||||
|
*
|
||||||
|
* @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com>
|
||||||
|
* @package system.caching
|
||||||
|
*/
|
||||||
|
class CEAcceleratorCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It checks the availability of eAccelerator.
|
||||||
|
* @throws CException if eAccelerator extension is not loaded, is disabled or the cache functions are not compiled in.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if(!function_exists('eaccelerator_get'))
|
||||||
|
throw new CException(Yii::t('yii','CEAcceleratorCache requires PHP eAccelerator extension to be loaded, enabled or compiled with the "--with-eaccelerator-shared-memory" option.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
$result = eaccelerator_get($key);
|
||||||
|
return $result !== NULL ? $result : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return eaccelerator_put($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return (NULL === eaccelerator_get($key)) ? $this->setValue($key,$value,$expire) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return eaccelerator_rm($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
// first, remove expired content from cache
|
||||||
|
eaccelerator_gc();
|
||||||
|
// now, remove leftover cache-keys
|
||||||
|
$keys = eaccelerator_list_keys();
|
||||||
|
foreach($keys as $key)
|
||||||
|
$this->deleteValue(substr($key['name'], 1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
242
framework/caching/CFileCache.php
Normal file
242
framework/caching/CFileCache.php
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CFileCache class file
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CFileCache provides a file-based caching mechanism.
|
||||||
|
*
|
||||||
|
* For each data value being cached, CFileCache will use store it in a separate file
|
||||||
|
* under {@link cachePath} which defaults to 'protected/runtime/cache'.
|
||||||
|
* CFileCache will perform garbage collection automatically to remove expired cache files.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CFileCache.
|
||||||
|
*
|
||||||
|
* @property integer $gCProbability The probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
*/
|
||||||
|
class CFileCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory to store cache files. Defaults to null, meaning
|
||||||
|
* using 'protected/runtime/cache' as the directory.
|
||||||
|
*/
|
||||||
|
public $cachePath;
|
||||||
|
/**
|
||||||
|
* @var string cache file suffix. Defaults to '.bin'.
|
||||||
|
*/
|
||||||
|
public $cacheFileSuffix='.bin';
|
||||||
|
/**
|
||||||
|
* @var integer the level of sub-directories to store cache files. Defaults to 0,
|
||||||
|
* meaning no sub-directories. If the system has huge number of cache files (e.g. 10K+),
|
||||||
|
* you may want to set this value to be 1 or 2 so that the file system is not over burdened.
|
||||||
|
* The value of this property should not exceed 16 (less than 3 is recommended).
|
||||||
|
*/
|
||||||
|
public $directoryLevel=0;
|
||||||
|
/**
|
||||||
|
* @var boolean whether cache entry expiration time should be embedded into a physical file.
|
||||||
|
* Defaults to false meaning that the file modification time will be used to store expire value.
|
||||||
|
* True value means that first ten bytes of the file would be reserved and used to store expiration time.
|
||||||
|
* On some systems PHP is not allowed to change file modification time to be in future even with 777
|
||||||
|
* permissions, so this property could be useful in this case.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public $embedExpiry=false;
|
||||||
|
|
||||||
|
private $_gcProbability=100;
|
||||||
|
private $_gced=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if($this->cachePath===null)
|
||||||
|
$this->cachePath=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache';
|
||||||
|
if(!is_dir($this->cachePath))
|
||||||
|
mkdir($this->cachePath,0777,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return integer the probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
*/
|
||||||
|
public function getGCProbability()
|
||||||
|
{
|
||||||
|
return $this->_gcProbability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param integer $value the probability (parts per million) that garbage collection (GC) should be performed
|
||||||
|
* when storing a piece of data in the cache. Defaults to 100, meaning 0.01% chance.
|
||||||
|
* This number should be between 0 and 1000000. A value 0 meaning no GC will be performed at all.
|
||||||
|
*/
|
||||||
|
public function setGCProbability($value)
|
||||||
|
{
|
||||||
|
$value=(int)$value;
|
||||||
|
if($value<0)
|
||||||
|
$value=0;
|
||||||
|
if($value>1000000)
|
||||||
|
$value=1000000;
|
||||||
|
$this->_gcProbability=$value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
$this->gc(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
$cacheFile=$this->getCacheFile($key);
|
||||||
|
if(($time=$this->filemtime($cacheFile))>time())
|
||||||
|
return @file_get_contents($cacheFile,false,null,$this->embedExpiry ? 10 : -1);
|
||||||
|
elseif($time>0)
|
||||||
|
@unlink($cacheFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if(!$this->_gced && mt_rand(0,1000000)<$this->_gcProbability)
|
||||||
|
{
|
||||||
|
$this->gc();
|
||||||
|
$this->_gced=true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($expire<=0)
|
||||||
|
$expire=31536000; // 1 year
|
||||||
|
$expire+=time();
|
||||||
|
|
||||||
|
$cacheFile=$this->getCacheFile($key);
|
||||||
|
if($this->directoryLevel>0)
|
||||||
|
@mkdir(dirname($cacheFile),0777,true);
|
||||||
|
if(@file_put_contents($cacheFile,$this->embedExpiry ? $expire.$value : $value,LOCK_EX)!==false)
|
||||||
|
{
|
||||||
|
@chmod($cacheFile,0777);
|
||||||
|
return $this->embedExpiry ? true : @touch($cacheFile,$expire);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
$cacheFile=$this->getCacheFile($key);
|
||||||
|
if($this->filemtime($cacheFile)>time())
|
||||||
|
return false;
|
||||||
|
return $this->setValue($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
$cacheFile=$this->getCacheFile($key);
|
||||||
|
return @unlink($cacheFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cache file path given the cache key.
|
||||||
|
* @param string $key cache key
|
||||||
|
* @return string the cache file path
|
||||||
|
*/
|
||||||
|
protected function getCacheFile($key)
|
||||||
|
{
|
||||||
|
if($this->directoryLevel>0)
|
||||||
|
{
|
||||||
|
$base=$this->cachePath;
|
||||||
|
for($i=0;$i<$this->directoryLevel;++$i)
|
||||||
|
{
|
||||||
|
if(($prefix=substr($key,$i+$i,2))!==false)
|
||||||
|
$base.=DIRECTORY_SEPARATOR.$prefix;
|
||||||
|
}
|
||||||
|
return $base.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $this->cachePath.DIRECTORY_SEPARATOR.$key.$this->cacheFileSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes expired cache files.
|
||||||
|
* @param boolean $expiredOnly whether only expired cache files should be removed.
|
||||||
|
* If false, all cache files under {@link cachePath} will be removed.
|
||||||
|
* @param string $path the path to clean with. If null, it will be {@link cachePath}.
|
||||||
|
*/
|
||||||
|
public function gc($expiredOnly=true,$path=null)
|
||||||
|
{
|
||||||
|
if($path===null)
|
||||||
|
$path=$this->cachePath;
|
||||||
|
if(($handle=opendir($path))===false)
|
||||||
|
return;
|
||||||
|
while(($file=readdir($handle))!==false)
|
||||||
|
{
|
||||||
|
if($file[0]==='.')
|
||||||
|
continue;
|
||||||
|
$fullPath=$path.DIRECTORY_SEPARATOR.$file;
|
||||||
|
if(is_dir($fullPath))
|
||||||
|
$this->gc($expiredOnly,$fullPath);
|
||||||
|
elseif($expiredOnly && $this->filemtime($fullPath)<time() || !$expiredOnly)
|
||||||
|
@unlink($fullPath);
|
||||||
|
}
|
||||||
|
closedir($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns cache file modification time. {@link $embedExpiry} aware.
|
||||||
|
* @param string $path to the file, modification time to be retrieved from.
|
||||||
|
* @return integer file modification time.
|
||||||
|
*/
|
||||||
|
private function filemtime($path)
|
||||||
|
{
|
||||||
|
if($this->embedExpiry)
|
||||||
|
return (int)@file_get_contents($path,false,null,0,10);
|
||||||
|
else
|
||||||
|
return @filemtime($path);
|
||||||
|
}
|
||||||
|
}
|
281
framework/caching/CMemCache.php
Normal file
281
framework/caching/CMemCache.php
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CMemCache class file
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CMemCache implements a cache application component based on {@link http://memcached.org/ memcached}.
|
||||||
|
*
|
||||||
|
* CMemCache can be configured with a list of memcache servers by settings
|
||||||
|
* its {@link setServers servers} property. By default, CMemCache assumes
|
||||||
|
* there is a memcache server running on localhost at port 11211.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CMemCache.
|
||||||
|
*
|
||||||
|
* Note, there is no security measure to protected data in memcache.
|
||||||
|
* All data in memcache can be accessed by any process running in the system.
|
||||||
|
*
|
||||||
|
* To use CMemCache as the cache application component, configure the application as follows,
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'components'=>array(
|
||||||
|
* 'cache'=>array(
|
||||||
|
* 'class'=>'CMemCache',
|
||||||
|
* 'servers'=>array(
|
||||||
|
* array(
|
||||||
|
* 'host'=>'server1',
|
||||||
|
* 'port'=>11211,
|
||||||
|
* 'weight'=>60,
|
||||||
|
* ),
|
||||||
|
* array(
|
||||||
|
* 'host'=>'server2',
|
||||||
|
* 'port'=>11211,
|
||||||
|
* 'weight'=>40,
|
||||||
|
* ),
|
||||||
|
* ),
|
||||||
|
* ),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
* In the above, two memcache servers are used: server1 and server2.
|
||||||
|
* You can configure more properties of every server, including:
|
||||||
|
* host, port, persistent, weight, timeout, retryInterval, status.
|
||||||
|
* See {@link http://www.php.net/manual/en/function.memcache-addserver.php}
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* CMemCache can also be used with {@link http://pecl.php.net/package/memcached memcached}.
|
||||||
|
* To do so, set {@link useMemcached} to be true.
|
||||||
|
*
|
||||||
|
* @property mixed $memCache The memcache instance (or memcached if {@link useMemcached} is true) used by this component.
|
||||||
|
* @property array $servers List of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CMemCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var boolean whether to use memcached or memcache as the underlying caching extension.
|
||||||
|
* If true {@link http://pecl.php.net/package/memcached memcached} will be used.
|
||||||
|
* If false {@link http://pecl.php.net/package/memcache memcache}. will be used.
|
||||||
|
* Defaults to false.
|
||||||
|
*/
|
||||||
|
public $useMemcached=false;
|
||||||
|
/**
|
||||||
|
* @var Memcache the Memcache instance
|
||||||
|
*/
|
||||||
|
private $_cache=null;
|
||||||
|
/**
|
||||||
|
* @var array list of memcache server configurations
|
||||||
|
*/
|
||||||
|
private $_servers=array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It creates the memcache instance and adds memcache servers.
|
||||||
|
* @throws CException if memcache extension is not loaded
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
$servers=$this->getServers();
|
||||||
|
$cache=$this->getMemCache();
|
||||||
|
if(count($servers))
|
||||||
|
{
|
||||||
|
foreach($servers as $server)
|
||||||
|
{
|
||||||
|
if($this->useMemcached)
|
||||||
|
$cache->addServer($server->host,$server->port,$server->weight);
|
||||||
|
else
|
||||||
|
$cache->addServer($server->host,$server->port,$server->persistent,$server->weight,$server->timeout,$server->retryInterval,$server->status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$cache->addServer('localhost',11211);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws CException if extension isn't loaded
|
||||||
|
* @return Memcache|Memcached the memcache instance (or memcached if {@link useMemcached} is true) used by this component.
|
||||||
|
*/
|
||||||
|
public function getMemCache()
|
||||||
|
{
|
||||||
|
if($this->_cache!==null)
|
||||||
|
return $this->_cache;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$extension=$this->useMemcached ? 'memcached' : 'memcache';
|
||||||
|
if(!extension_loaded($extension))
|
||||||
|
throw new CException(Yii::t('yii',"CMemCache requires PHP {extension} extension to be loaded.",
|
||||||
|
array('{extension}'=>$extension)));
|
||||||
|
return $this->_cache=$this->useMemcached ? new Memcached : new Memcache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array list of memcache server configurations. Each element is a {@link CMemCacheServerConfiguration}.
|
||||||
|
*/
|
||||||
|
public function getServers()
|
||||||
|
{
|
||||||
|
return $this->_servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $config list of memcache server configurations. Each element must be an array
|
||||||
|
* with the following keys: host, port, persistent, weight, timeout, retryInterval, status.
|
||||||
|
* @see http://www.php.net/manual/en/function.Memcache-addServer.php
|
||||||
|
*/
|
||||||
|
public function setServers($config)
|
||||||
|
{
|
||||||
|
foreach($config as $c)
|
||||||
|
$this->_servers[]=new CMemCacheServerConfiguration($c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
return $this->_cache->get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
return $this->useMemcached ? $this->_cache->getMulti($keys) : $this->_cache->get($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if($expire>0)
|
||||||
|
$expire+=time();
|
||||||
|
else
|
||||||
|
$expire=0;
|
||||||
|
|
||||||
|
return $this->useMemcached ? $this->_cache->set($key,$value,$expire) : $this->_cache->set($key,$value,0,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if($expire>0)
|
||||||
|
$expire+=time();
|
||||||
|
else
|
||||||
|
$expire=0;
|
||||||
|
|
||||||
|
return $this->useMemcached ? $this->_cache->add($key,$value,$expire) : $this->_cache->add($key,$value,0,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return $this->_cache->delete($key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
return $this->_cache->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CMemCacheServerConfiguration represents the configuration data for a single memcache server.
|
||||||
|
*
|
||||||
|
* See {@link http://www.php.net/manual/en/function.Memcache-addServer.php}
|
||||||
|
* for detailed explanation of each configuration property.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CMemCacheServerConfiguration extends CComponent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string memcache server hostname or IP address
|
||||||
|
*/
|
||||||
|
public $host;
|
||||||
|
/**
|
||||||
|
* @var integer memcache server port
|
||||||
|
*/
|
||||||
|
public $port=11211;
|
||||||
|
/**
|
||||||
|
* @var boolean whether to use a persistent connection
|
||||||
|
*/
|
||||||
|
public $persistent=true;
|
||||||
|
/**
|
||||||
|
* @var integer probability of using this server among all servers.
|
||||||
|
*/
|
||||||
|
public $weight=1;
|
||||||
|
/**
|
||||||
|
* @var integer value in seconds which will be used for connecting to the server
|
||||||
|
*/
|
||||||
|
public $timeout=15;
|
||||||
|
/**
|
||||||
|
* @var integer how often a failed server will be retried (in seconds)
|
||||||
|
*/
|
||||||
|
public $retryInterval=15;
|
||||||
|
/**
|
||||||
|
* @var boolean if the server should be flagged as online upon a failure
|
||||||
|
*/
|
||||||
|
public $status=true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param array $config list of memcache server configurations.
|
||||||
|
* @throws CException if the configuration is not an array
|
||||||
|
*/
|
||||||
|
public function __construct($config)
|
||||||
|
{
|
||||||
|
if(is_array($config))
|
||||||
|
{
|
||||||
|
foreach($config as $key=>$value)
|
||||||
|
$this->$key=$value;
|
||||||
|
if($this->host===null)
|
||||||
|
throw new CException(Yii::t('yii','CMemCache server configuration must have "host" value.'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CMemCache server configuration must be an array.'));
|
||||||
|
}
|
||||||
|
}
|
257
framework/caching/CRedisCache.php
Normal file
257
framework/caching/CRedisCache.php
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CRedisCache class file
|
||||||
|
*
|
||||||
|
* @author Carsten Brandt <mail@cebe.cc>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRedisCache implements a cache application component based on {@link http://redis.io/ redis}.
|
||||||
|
*
|
||||||
|
* CRedisCache needs to be configured with {@link hostname}, {@link port} and {@link database} of the server
|
||||||
|
* to connect to. By default CRedisCache assumes there is a redis server running on localhost at
|
||||||
|
* port 6379 and uses the database number 0.
|
||||||
|
*
|
||||||
|
* CRedisCache also supports {@link http://redis.io/commands/auth the AUTH command} of redis.
|
||||||
|
* When the server needs authentication, you can set the {@link password} property to
|
||||||
|
* authenticate with the server after connect.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CRedisCache.
|
||||||
|
*
|
||||||
|
* To use CRedisCache as the cache application component, configure the application as follows,
|
||||||
|
* <pre>
|
||||||
|
* array(
|
||||||
|
* 'components'=>array(
|
||||||
|
* 'cache'=>array(
|
||||||
|
* 'class'=>'CRedisCache',
|
||||||
|
* 'hostname'=>'localhost',
|
||||||
|
* 'port'=>6379,
|
||||||
|
* 'database'=>0,
|
||||||
|
* ),
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The minimum required redis version is 2.0.0.
|
||||||
|
*
|
||||||
|
* @author Carsten Brandt <mail@cebe.cc>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
class CRedisCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string hostname to use for connecting to the redis server. Defaults to 'localhost'.
|
||||||
|
*/
|
||||||
|
public $hostname='localhost';
|
||||||
|
/**
|
||||||
|
* @var int the port to use for connecting to the redis server. Default port is 6379.
|
||||||
|
*/
|
||||||
|
public $port=6379;
|
||||||
|
/**
|
||||||
|
* @var string the password to use to authenticate with the redis server. If not set, no AUTH command will be sent.
|
||||||
|
*/
|
||||||
|
public $password;
|
||||||
|
/**
|
||||||
|
* @var int the redis database to use. This is an integer value starting from 0. Defaults to 0.
|
||||||
|
*/
|
||||||
|
public $database=0;
|
||||||
|
/**
|
||||||
|
* @var float timeout to use for connection to redis. If not set the timeout set in php.ini will be used: ini_get("default_socket_timeout")
|
||||||
|
*/
|
||||||
|
public $timeout=null;
|
||||||
|
/**
|
||||||
|
* @var resource redis socket connection
|
||||||
|
*/
|
||||||
|
private $_socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a connection to the redis server.
|
||||||
|
* It does nothing if the connection has already been established.
|
||||||
|
* @throws CException if connecting fails
|
||||||
|
*/
|
||||||
|
protected function connect()
|
||||||
|
{
|
||||||
|
$this->_socket=@stream_socket_client(
|
||||||
|
$this->hostname.':'.$this->port,
|
||||||
|
$errorNumber,
|
||||||
|
$errorDescription,
|
||||||
|
$this->timeout ? $this->timeout : ini_get("default_socket_timeout")
|
||||||
|
);
|
||||||
|
if ($this->_socket)
|
||||||
|
{
|
||||||
|
if($this->password!==null)
|
||||||
|
$this->executeCommand('AUTH',array($this->password));
|
||||||
|
$this->executeCommand('SELECT',array($this->database));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException('Failed to connect to redis: '.$errorDescription,(int)$errorNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a redis command.
|
||||||
|
* For a list of available commands and their parameters see {@link http://redis.io/commands}.
|
||||||
|
*
|
||||||
|
* @param string $name the name of the command
|
||||||
|
* @param array $params list of parameters for the command
|
||||||
|
* @return array|bool|null|string Dependend on the executed command this method
|
||||||
|
* will return different data types:
|
||||||
|
* <ul>
|
||||||
|
* <li><code>true</code> for commands that return "status reply".</li>
|
||||||
|
* <li><code>string</code> for commands that return "integer reply"
|
||||||
|
* as the value is in the range of a signed 64 bit integer.</li>
|
||||||
|
* <li><code>string</code> or <code>null</code> for commands that return "bulk reply".</li>
|
||||||
|
* <li><code>array</code> for commands that return "Multi-bulk replies".</li>
|
||||||
|
* </ul>
|
||||||
|
* See {@link http://redis.io/topics/protocol redis protocol description}
|
||||||
|
* for details on the mentioned reply types.
|
||||||
|
* @trows CException for commands that return {@link http://redis.io/topics/protocol#error-reply error reply}.
|
||||||
|
*/
|
||||||
|
public function executeCommand($name,$params=array())
|
||||||
|
{
|
||||||
|
if($this->_socket===null)
|
||||||
|
$this->connect();
|
||||||
|
|
||||||
|
array_unshift($params,$name);
|
||||||
|
$command='*'.count($params)."\r\n";
|
||||||
|
foreach($params as $arg)
|
||||||
|
$command.='$'.strlen($arg)."\r\n".$arg."\r\n";
|
||||||
|
|
||||||
|
fwrite($this->_socket,$command);
|
||||||
|
|
||||||
|
return $this->parseResponse(implode(' ',$params));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the result from socket and parses it
|
||||||
|
* @return array|bool|null|string
|
||||||
|
* @throws CException socket or data problems
|
||||||
|
*/
|
||||||
|
private function parseResponse()
|
||||||
|
{
|
||||||
|
if(($line=fgets($this->_socket))===false)
|
||||||
|
throw new CException('Failed reading data from redis connection socket.');
|
||||||
|
$type=$line[0];
|
||||||
|
$line=substr($line,1,-2);
|
||||||
|
switch($type)
|
||||||
|
{
|
||||||
|
case '+': // Status reply
|
||||||
|
return true;
|
||||||
|
case '-': // Error reply
|
||||||
|
throw new CException('Redis error: '.$line);
|
||||||
|
case ':': // Integer reply
|
||||||
|
// no cast to int as it is in the range of a signed 64 bit integer
|
||||||
|
return $line;
|
||||||
|
case '$': // Bulk replies
|
||||||
|
if($line=='-1')
|
||||||
|
return null;
|
||||||
|
$length=$line+2;
|
||||||
|
$data='';
|
||||||
|
while($length>0)
|
||||||
|
{
|
||||||
|
if(($block=fread($this->_socket,$length))===false)
|
||||||
|
throw new CException('Failed reading data from redis connection socket.');
|
||||||
|
$data.=$block;
|
||||||
|
$length-=(function_exists('mb_strlen') ? mb_strlen($block,'8bit') : strlen($block));
|
||||||
|
}
|
||||||
|
return substr($data,0,-2);
|
||||||
|
case '*': // Multi-bulk replies
|
||||||
|
$count=(int)$line;
|
||||||
|
$data=array();
|
||||||
|
for($i=0;$i<$count;$i++)
|
||||||
|
$data[]=$this->parseResponse();
|
||||||
|
return $data;
|
||||||
|
default:
|
||||||
|
throw new CException('Unable to parse data received from redis.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
return $this->executeCommand('GET',array($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
$response=$this->executeCommand('MGET',$keys);
|
||||||
|
$result=array();
|
||||||
|
$i=0;
|
||||||
|
foreach($keys as $key)
|
||||||
|
$result[$key]=$response[$i++];
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if ($expire==0)
|
||||||
|
return (bool)$this->executeCommand('SET',array($key,$value));
|
||||||
|
return (bool)$this->executeCommand('SETEX',array($key,$expire,$value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
if ($expire == 0)
|
||||||
|
return (bool)$this->executeCommand('SETNX',array($key,$value));
|
||||||
|
|
||||||
|
if($this->executeCommand('SETNX',array($key,$value)))
|
||||||
|
{
|
||||||
|
$this->executeCommand('EXPIRE',array($key,$expire));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return (bool)$this->executeCommand('DEL',array($key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
return $this->executeCommand('FLUSHDB');
|
||||||
|
}
|
||||||
|
}
|
108
framework/caching/CWinCache.php
Normal file
108
framework/caching/CWinCache.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CWinCache class file
|
||||||
|
*
|
||||||
|
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CWinCache implements a cache application component based on {@link http://www.iis.net/expand/wincacheforphp WinCache}.
|
||||||
|
*
|
||||||
|
* To use this application component, the WinCache PHP extension must be loaded.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CWinCache.
|
||||||
|
*
|
||||||
|
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||||
|
* @package system.caching
|
||||||
|
* @since 1.1.2
|
||||||
|
*/
|
||||||
|
class CWinCache extends CCache {
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It checks the availability of WinCache extension and WinCache user cache.
|
||||||
|
* @throws CException if WinCache extension is not loaded or user cache is disabled
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if(!extension_loaded('wincache'))
|
||||||
|
throw new CException(Yii::t('yii', 'CWinCache requires PHP wincache extension to be loaded.'));
|
||||||
|
if(!ini_get('wincache.ucenabled'))
|
||||||
|
throw new CException(Yii::t('yii', 'CWinCache user cache is disabled. Please set wincache.ucenabled to On in your php.ini.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
return wincache_ucache_get($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves multiple values from cache with the specified keys.
|
||||||
|
* @param array $keys a list of keys identifying the cached values
|
||||||
|
* @return array a list of cached values indexed by the keys
|
||||||
|
*/
|
||||||
|
protected function getValues($keys)
|
||||||
|
{
|
||||||
|
return wincache_ucache_get($keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return wincache_ucache_set($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return wincache_ucache_add($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return wincache_ucache_delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
return wincache_ucache_clear();
|
||||||
|
}
|
||||||
|
}
|
103
framework/caching/CXCache.php
Normal file
103
framework/caching/CXCache.php
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CXCache class file
|
||||||
|
*
|
||||||
|
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CXCache implements a cache application module based on {@link http://xcache.lighttpd.net/ xcache}.
|
||||||
|
*
|
||||||
|
* To use this application component, the XCache PHP extension must be loaded.
|
||||||
|
* Flush functionality will only work correctly if "xcache.admin.enable_auth" is set to "Off" in php.ini.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CXCache.
|
||||||
|
*
|
||||||
|
* @author Wei Zhuo <weizhuo[at]gmail[dot]com>
|
||||||
|
* @package system.caching
|
||||||
|
*/
|
||||||
|
class CXCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It checks the availability of memcache.
|
||||||
|
* @throws CException if memcache extension is not loaded or is disabled.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if(!function_exists('xcache_isset'))
|
||||||
|
throw new CException(Yii::t('yii','CXCache requires PHP XCache extension to be loaded.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
return xcache_isset($key) ? xcache_get($key) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return xcache_set($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return !xcache_isset($key) ? $this->setValue($key,$value,$expire) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return xcache_unset($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
for($i=0, $max=xcache_count(XC_TYPE_VAR); $i<$max; $i++)
|
||||||
|
{
|
||||||
|
if(xcache_clear_cache(XC_TYPE_VAR, $i)===false)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
98
framework/caching/CZendDataCache.php
Normal file
98
framework/caching/CZendDataCache.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CZendDataCache class file
|
||||||
|
*
|
||||||
|
* @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CZendDataCache implements a cache application module based on the Zend Data Cache
|
||||||
|
* delivered with {@link http://www.zend.com/en/products/server/ ZendServer}.
|
||||||
|
*
|
||||||
|
* To use this application component, the Zend Data Cache PHP extension must be loaded.
|
||||||
|
*
|
||||||
|
* See {@link CCache} manual for common cache operations that are supported by CZendDataCache.
|
||||||
|
*
|
||||||
|
* @author Steffen Dietz <steffo.dietz[at]googlemail[dot]com>
|
||||||
|
* @package system.caching
|
||||||
|
*/
|
||||||
|
class CZendDataCache extends CCache
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Initializes this application component.
|
||||||
|
* This method is required by the {@link IApplicationComponent} interface.
|
||||||
|
* It checks the availability of Zend Data Cache.
|
||||||
|
* @throws CException if Zend Data Cache extension is not loaded.
|
||||||
|
*/
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
parent::init();
|
||||||
|
if(!function_exists('zend_shm_cache_store'))
|
||||||
|
throw new CException(Yii::t('yii','CZendDataCache requires PHP Zend Data Cache extension to be loaded.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a value from cache with a specified key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key a unique key identifying the cached value
|
||||||
|
* @return string|boolean the value stored in cache, false if the value is not in the cache or expired.
|
||||||
|
*/
|
||||||
|
protected function getValue($key)
|
||||||
|
{
|
||||||
|
$result = zend_shm_cache_fetch($key);
|
||||||
|
return $result !== NULL ? $result : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key in cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function setValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return zend_shm_cache_store($key,$value,$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a value identified by a key into cache if the cache does not contain this key.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
*
|
||||||
|
* @param string $key the key identifying the value to be cached
|
||||||
|
* @param string $value the value to be cached
|
||||||
|
* @param integer $expire the number of seconds in which the cached value will expire. 0 means never expire.
|
||||||
|
* @return boolean true if the value is successfully stored into cache, false otherwise
|
||||||
|
*/
|
||||||
|
protected function addValue($key,$value,$expire)
|
||||||
|
{
|
||||||
|
return (NULL === zend_shm_cache_fetch($key)) ? $this->setValue($key,$value,$expire) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value with the specified key from cache
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @param string $key the key of the value to be deleted
|
||||||
|
* @return boolean if no error happens during deletion
|
||||||
|
*/
|
||||||
|
protected function deleteValue($key)
|
||||||
|
{
|
||||||
|
return zend_shm_cache_delete($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all values from cache.
|
||||||
|
* This is the implementation of the method declared in the parent class.
|
||||||
|
* @return boolean whether the flush operation was successful.
|
||||||
|
* @since 1.1.5
|
||||||
|
*/
|
||||||
|
protected function flushValues()
|
||||||
|
{
|
||||||
|
return zend_shm_cache_clear();
|
||||||
|
}
|
||||||
|
}
|
117
framework/caching/dependencies/CCacheDependency.php
Normal file
117
framework/caching/dependencies/CCacheDependency.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CCacheDependency is the base class for cache dependency classes.
|
||||||
|
*
|
||||||
|
* CCacheDependency implements the {@link ICacheDependency} interface.
|
||||||
|
* Child classes should override its {@link generateDependentData} for
|
||||||
|
* actual dependency checking.
|
||||||
|
*
|
||||||
|
* @property boolean $hasChanged Whether the dependency has changed.
|
||||||
|
* @property mixed $dependentData The data used to determine if dependency has been changed.
|
||||||
|
* This data is available after {@link evaluateDependency} is called.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CCacheDependency extends CComponent implements ICacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var boolean Whether this dependency is reusable or not.
|
||||||
|
* If set to true, dependent data for this cache dependency will only be generated once per request.
|
||||||
|
* You can then use the same cache dependency for multiple separate cache calls on the same page
|
||||||
|
* without the overhead of re-evaluating the dependency each time.
|
||||||
|
* Defaults to false;
|
||||||
|
* @since 1.1.11
|
||||||
|
*/
|
||||||
|
public $reuseDependentData=false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array cached data for reusable dependencies.
|
||||||
|
* @since 1.1.11
|
||||||
|
*/
|
||||||
|
private static $_reusableData=array();
|
||||||
|
|
||||||
|
private $_hash;
|
||||||
|
private $_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the dependency by generating and saving the data related with dependency.
|
||||||
|
* This method is invoked by cache before writing data into it.
|
||||||
|
*/
|
||||||
|
public function evaluateDependency()
|
||||||
|
{
|
||||||
|
if ($this->reuseDependentData)
|
||||||
|
{
|
||||||
|
$hash=$this->getHash();
|
||||||
|
if(!isset(self::$_reusableData[$hash]['dependentData']))
|
||||||
|
self::$_reusableData[$hash]['dependentData']=$this->generateDependentData();
|
||||||
|
$this->_data=self::$_reusableData[$hash]['dependentData'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$this->_data=$this->generateDependentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean whether the dependency has changed.
|
||||||
|
*/
|
||||||
|
public function getHasChanged()
|
||||||
|
{
|
||||||
|
if ($this->reuseDependentData)
|
||||||
|
{
|
||||||
|
$hash=$this->getHash();
|
||||||
|
if(!isset(self::$_reusableData[$hash]['dependentData']))
|
||||||
|
self::$_reusableData[$hash]['dependentData']=$this->generateDependentData();
|
||||||
|
return self::$_reusableData[$hash]['dependentData']!=$this->_data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $this->generateDependentData()!=$this->_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed the data used to determine if dependency has been changed.
|
||||||
|
* This data is available after {@link evaluateDependency} is called.
|
||||||
|
*/
|
||||||
|
public function getDependentData()
|
||||||
|
{
|
||||||
|
return $this->_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets cached data for reusable dependencies.
|
||||||
|
* @since 1.1.14
|
||||||
|
*/
|
||||||
|
public static function resetReusableData()
|
||||||
|
{
|
||||||
|
self::$_reusableData=array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* Derived classes should override this method to generate actual dependent data.
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generates a unique hash that identifies this cache dependency.
|
||||||
|
* @return string the hash for this cache dependency
|
||||||
|
*/
|
||||||
|
private function getHash()
|
||||||
|
{
|
||||||
|
if($this->_hash===null)
|
||||||
|
$this->_hash=sha1(serialize($this));
|
||||||
|
return $this->_hash;
|
||||||
|
}
|
||||||
|
}
|
97
framework/caching/dependencies/CChainedCacheDependency.php
Normal file
97
framework/caching/dependencies/CChainedCacheDependency.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CChainedCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CChainedCacheDependency represents a list of cache dependencies.
|
||||||
|
*
|
||||||
|
* If any of the dependencies reports a dependency change, CChainedCacheDependency
|
||||||
|
* will return true for the checking.
|
||||||
|
*
|
||||||
|
* To add dependencies to CChainedCacheDependency, use {@link getDependencies Dependencies}
|
||||||
|
* which gives a {@link CTypedList} instance and can be used like an array
|
||||||
|
* (see {@link CList} for more details}).
|
||||||
|
*
|
||||||
|
* @property CTypedList $dependencies List of dependency objects.
|
||||||
|
* @property boolean $hasChanged Whether the dependency is changed or not.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CChainedCacheDependency extends CComponent implements ICacheDependency
|
||||||
|
{
|
||||||
|
private $_dependencies=null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param array $dependencies the dependencies to be added to this chain.
|
||||||
|
* @since 1.1.4
|
||||||
|
*/
|
||||||
|
public function __construct($dependencies=array())
|
||||||
|
{
|
||||||
|
if(!empty($dependencies))
|
||||||
|
$this->setDependencies($dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CTypedList list of dependency objects
|
||||||
|
*/
|
||||||
|
public function getDependencies()
|
||||||
|
{
|
||||||
|
if($this->_dependencies===null)
|
||||||
|
$this->_dependencies=new CTypedList('ICacheDependency');
|
||||||
|
return $this->_dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $values list of dependency objects or configurations to be added to this chain.
|
||||||
|
* If a dependency is specified as a configuration, it must be an array that can be recognized
|
||||||
|
* by {@link YiiBase::createComponent}.
|
||||||
|
*/
|
||||||
|
public function setDependencies($values)
|
||||||
|
{
|
||||||
|
$dependencies=$this->getDependencies();
|
||||||
|
foreach($values as $value)
|
||||||
|
{
|
||||||
|
if(is_array($value))
|
||||||
|
$value=Yii::createComponent($value);
|
||||||
|
$dependencies->add($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the dependency by generating and saving the data related with dependency.
|
||||||
|
*/
|
||||||
|
public function evaluateDependency()
|
||||||
|
{
|
||||||
|
if($this->_dependencies!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_dependencies as $dependency)
|
||||||
|
$dependency->evaluateDependency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the actual dependency checking.
|
||||||
|
* This method returns true if any of the dependency objects
|
||||||
|
* reports a dependency change.
|
||||||
|
* @return boolean whether the dependency is changed or not.
|
||||||
|
*/
|
||||||
|
public function getHasChanged()
|
||||||
|
{
|
||||||
|
if($this->_dependencies!==null)
|
||||||
|
{
|
||||||
|
foreach($this->_dependencies as $dependency)
|
||||||
|
if($dependency->getHasChanged())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
112
framework/caching/dependencies/CDbCacheDependency.php
Normal file
112
framework/caching/dependencies/CDbCacheDependency.php
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CDbCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDbCacheDependency represents a dependency based on the query result of a SQL statement.
|
||||||
|
*
|
||||||
|
* If the query result (a scalar) changes, the dependency is considered as changed.
|
||||||
|
* To specify the SQL statement, set {@link sql} property.
|
||||||
|
* The {@link connectionID} property specifies the ID of a {@link CDbConnection} application
|
||||||
|
* component. It is this DB connection that is used to perform the query.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CDbCacheDependency extends CCacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the ID of a {@link CDbConnection} application component. Defaults to 'db'.
|
||||||
|
*/
|
||||||
|
public $connectionID='db';
|
||||||
|
/**
|
||||||
|
* @var string the SQL statement whose result is used to determine if the dependency has been changed.
|
||||||
|
* Note, the SQL statement should return back a single value.
|
||||||
|
*/
|
||||||
|
public $sql;
|
||||||
|
/**
|
||||||
|
* @var array parameters (name=>value) to be bound to the SQL statement specified by {@link sql}.
|
||||||
|
* @since 1.1.4
|
||||||
|
*/
|
||||||
|
public $params;
|
||||||
|
|
||||||
|
private $_db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $sql the SQL statement whose result is used to determine if the dependency has been changed.
|
||||||
|
*/
|
||||||
|
public function __construct($sql=null)
|
||||||
|
{
|
||||||
|
$this->sql=$sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PHP sleep magic method.
|
||||||
|
* This method ensures that the database instance is set null because it contains resource handles.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function __sleep()
|
||||||
|
{
|
||||||
|
$this->_db=null;
|
||||||
|
return array_keys((array)$this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* This method returns the value of the global state.
|
||||||
|
* @throws CException if {@link sql} is empty
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
if($this->sql!==null)
|
||||||
|
{
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
$command=$db->createCommand($this->sql);
|
||||||
|
if(is_array($this->params))
|
||||||
|
{
|
||||||
|
foreach($this->params as $name=>$value)
|
||||||
|
$command->bindValue($name,$value);
|
||||||
|
}
|
||||||
|
if($db->queryCachingDuration>0)
|
||||||
|
{
|
||||||
|
// temporarily disable and re-enable query caching
|
||||||
|
$duration=$db->queryCachingDuration;
|
||||||
|
$db->queryCachingDuration=0;
|
||||||
|
$result=$command->queryRow();
|
||||||
|
$db->queryCachingDuration=$duration;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$result=$command->queryRow();
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CDbCacheDependency.sql cannot be empty.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CDbConnection the DB connection instance
|
||||||
|
* @throws CException if {@link connectionID} does not point to a valid application component.
|
||||||
|
*/
|
||||||
|
protected function getDbConnection()
|
||||||
|
{
|
||||||
|
if($this->_db!==null)
|
||||||
|
return $this->_db;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
|
||||||
|
return $this->_db;
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CDbCacheDependency.connectionID "{id}" is invalid. Please make sure it refers to the ID of a CDbConnection application component.',
|
||||||
|
array('{id}'=>$this->connectionID)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
135
framework/caching/dependencies/CDirectoryCacheDependency.php
Normal file
135
framework/caching/dependencies/CDirectoryCacheDependency.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CDirectoryCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CDirectoryCacheDependency represents a dependency based on change of a directory.
|
||||||
|
*
|
||||||
|
* CDirectoryCacheDependency performs dependency checking based on the
|
||||||
|
* modification time of the files contained in the specified directory.
|
||||||
|
* The directory being checked is specified via {@link directory}.
|
||||||
|
*
|
||||||
|
* By default, all files under the specified directory and subdirectories
|
||||||
|
* will be checked. If the last modification time of any of them is changed
|
||||||
|
* or if different number of files are contained in a directory, the dependency
|
||||||
|
* is reported as changed. By specifying {@link recursiveLevel},
|
||||||
|
* one can limit the checking to a certain depth of the directory.
|
||||||
|
*
|
||||||
|
* Note, dependency checking for a directory is expensive because it involves
|
||||||
|
* accessing modification time of multiple files under the directory.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CDirectoryCacheDependency extends CCacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory whose change is used to determine if the dependency has been changed.
|
||||||
|
* If any of the files under the directory is changed, the dependency is considered as changed.
|
||||||
|
*/
|
||||||
|
public $directory;
|
||||||
|
/**
|
||||||
|
* @var integer the depth of the subdirectories to be recursively checked.
|
||||||
|
* If the value is less than 0, it means unlimited depth.
|
||||||
|
* If the value is 0, it means checking the files directly under the specified directory.
|
||||||
|
*/
|
||||||
|
public $recursiveLevel=-1;
|
||||||
|
/**
|
||||||
|
* @var string the regular expression matching valid file/directory names.
|
||||||
|
* Only the matching files or directories will be checked for changes.
|
||||||
|
* Defaults to null, meaning all files/directories will qualify.
|
||||||
|
*/
|
||||||
|
public $namePattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $directory the directory to be checked
|
||||||
|
*/
|
||||||
|
public function __construct($directory=null)
|
||||||
|
{
|
||||||
|
$this->directory=$directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* This method returns the modification timestamps for files under the directory.
|
||||||
|
* @throws CException if {@link directory} is empty
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
if($this->directory!==null)
|
||||||
|
return $this->generateTimestamps($this->directory);
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CDirectoryCacheDependency.directory cannot be empty.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the last modification time for files under the directory.
|
||||||
|
* This method may go recursively into subdirectories if {@link recursiveLevel} is not 0.
|
||||||
|
* @param string $directory the directory name
|
||||||
|
* @param integer $level level of the recursion
|
||||||
|
* @throws CException if given directory is not valid
|
||||||
|
* @return array list of file modification time indexed by the file path
|
||||||
|
*/
|
||||||
|
protected function generateTimestamps($directory,$level=0)
|
||||||
|
{
|
||||||
|
if(($dir=@opendir($directory))===false)
|
||||||
|
throw new CException(Yii::t('yii','"{path}" is not a valid directory.',
|
||||||
|
array('{path}'=>$directory)));
|
||||||
|
$timestamps=array();
|
||||||
|
while(($file=readdir($dir))!==false)
|
||||||
|
{
|
||||||
|
$path=$directory.DIRECTORY_SEPARATOR.$file;
|
||||||
|
if($file==='.' || $file==='..')
|
||||||
|
continue;
|
||||||
|
if($this->namePattern!==null && !preg_match($this->namePattern,$file))
|
||||||
|
continue;
|
||||||
|
if(is_file($path))
|
||||||
|
{
|
||||||
|
if($this->validateFile($path))
|
||||||
|
$timestamps[$path]=filemtime($path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($this->recursiveLevel<0 || $level<$this->recursiveLevel) && $this->validateDirectory($path))
|
||||||
|
$timestamps=array_merge($timestamps, $this->generateTimestamps($path,$level+1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir($dir);
|
||||||
|
return $timestamps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the file should be checked for dependency.
|
||||||
|
* This method is invoked when dependency of the whole directory is being checked.
|
||||||
|
* By default, it always returns true, meaning the file should be checked.
|
||||||
|
* You may override this method to check only certain files.
|
||||||
|
* @param string $fileName the name of the file that may be checked for dependency.
|
||||||
|
* @return boolean whether this file should be checked.
|
||||||
|
*/
|
||||||
|
protected function validateFile($fileName)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if the specified subdirectory should be checked for dependency.
|
||||||
|
* This method is invoked when dependency of the whole directory is being checked.
|
||||||
|
* By default, it always returns true, meaning the subdirectory should be checked.
|
||||||
|
* You may override this method to check only certain subdirectories.
|
||||||
|
* @param string $directory the name of the subdirectory that may be checked for dependency.
|
||||||
|
* @return boolean whether this subdirectory should be checked.
|
||||||
|
*/
|
||||||
|
protected function validateDirectory($directory)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
55
framework/caching/dependencies/CExpressionDependency.php
Normal file
55
framework/caching/dependencies/CExpressionDependency.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CExpressionDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CExpressionDependency represents a dependency based on the result of a PHP expression.
|
||||||
|
*
|
||||||
|
* CExpressionDependency performs dependency checking based on the
|
||||||
|
* result of a PHP {@link expression}.
|
||||||
|
* The dependency is reported as unchanged if and only if the result is
|
||||||
|
* the same as the one evaluated when storing the data to cache.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CExpressionDependency extends CCacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the PHP expression whose result is used to determine the dependency.
|
||||||
|
* The expression can also be a valid serializable PHP callback.
|
||||||
|
* It will be passed with a parameter which is the dependency object itself.
|
||||||
|
*
|
||||||
|
* The PHP expression will be evaluated using {@link evaluateExpression}.
|
||||||
|
*
|
||||||
|
* A PHP expression can be any PHP code that has a value. To learn more about what an expression is,
|
||||||
|
* please refer to the {@link http://www.php.net/manual/en/language.expressions.php php manual}.
|
||||||
|
*/
|
||||||
|
public $expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $expression the PHP expression whose result is used to determine the dependency.
|
||||||
|
*/
|
||||||
|
public function __construct($expression='true')
|
||||||
|
{
|
||||||
|
$this->expression=$expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* This method returns the result of the PHP expression.
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
return $this->evaluateExpression($this->expression);
|
||||||
|
}
|
||||||
|
}
|
53
framework/caching/dependencies/CFileCacheDependency.php
Normal file
53
framework/caching/dependencies/CFileCacheDependency.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CFileCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CFileCacheDependency represents a dependency based on a file's last modification time.
|
||||||
|
*
|
||||||
|
* CFileCacheDependency performs dependency checking based on the
|
||||||
|
* last modification time of the file specified via {@link fileName}.
|
||||||
|
* The dependency is reported as unchanged if and only if the file's
|
||||||
|
* last modification time remains unchanged.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CFileCacheDependency extends CCacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the name of the file whose last modification time is used to
|
||||||
|
* check if the dependency has been changed.
|
||||||
|
*/
|
||||||
|
public $fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $fileName name of the file whose change is to be checked.
|
||||||
|
*/
|
||||||
|
public function __construct($fileName=null)
|
||||||
|
{
|
||||||
|
$this->fileName=$fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* This method returns the file's last modification time.
|
||||||
|
* @throws CException if {@link fileName} is empty
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
if($this->fileName!==null)
|
||||||
|
return @filemtime($this->fileName);
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CFileCacheDependency.fileName cannot be empty.'));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CGlobalStateCacheDependency class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CGlobalStateCacheDependency represents a dependency based on a global state value.
|
||||||
|
*
|
||||||
|
* CGlobalStateCacheDependency checks if a global state is changed or not.
|
||||||
|
* If the global state is changed, the dependency is reported as changed.
|
||||||
|
* To specify which global state this dependency should check with,
|
||||||
|
* set {@link stateName} to the name of the global state.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.caching.dependencies
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CGlobalStateCacheDependency extends CCacheDependency
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the name of the global state whose value is to check
|
||||||
|
* if the dependency has changed.
|
||||||
|
* @see CApplication::setGlobalState
|
||||||
|
*/
|
||||||
|
public $stateName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
* @param string $name the name of the global state
|
||||||
|
*/
|
||||||
|
public function __construct($name=null)
|
||||||
|
{
|
||||||
|
$this->stateName=$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the data needed to determine if dependency has been changed.
|
||||||
|
* This method returns the value of the global state.
|
||||||
|
* @throws CException if {@link stateName} is empty
|
||||||
|
* @return mixed the data needed to determine if dependency has been changed.
|
||||||
|
*/
|
||||||
|
protected function generateDependentData()
|
||||||
|
{
|
||||||
|
if($this->stateName!==null)
|
||||||
|
return Yii::app()->getGlobalState($this->stateName);
|
||||||
|
else
|
||||||
|
throw new CException(Yii::t('yii','CGlobalStateCacheDependency.stateName cannot be empty.'));
|
||||||
|
}
|
||||||
|
}
|
228
framework/cli/commands/MessageCommand.php
Normal file
228
framework/cli/commands/MessageCommand.php
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MessageCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MessageCommand extracts messages to be translated from source files.
|
||||||
|
* The extracted messages are saved as PHP message source files
|
||||||
|
* under the specified directory.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class MessageCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
yiic message <config-file>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command searches for messages to be translated in the specified
|
||||||
|
source files and compiles them into PHP arrays as message source.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* config-file: required, the path of the configuration file. You can find
|
||||||
|
an example in framework/messages/config.php.
|
||||||
|
|
||||||
|
The file can be placed anywhere and must be a valid PHP script which
|
||||||
|
returns an array of name-value pairs. Each name-value pair represents
|
||||||
|
a configuration option.
|
||||||
|
|
||||||
|
The following options are available:
|
||||||
|
|
||||||
|
- sourcePath: string, root directory of all source files.
|
||||||
|
- messagePath: string, root directory containing message translations.
|
||||||
|
- languages: array, list of language codes that the extracted messages
|
||||||
|
should be translated to. For example, array('zh_cn','en_au').
|
||||||
|
- fileTypes: array, a list of file extensions (e.g. 'php', 'xml').
|
||||||
|
Only the files whose extension name can be found in this list
|
||||||
|
will be processed. If empty, all files will be processed.
|
||||||
|
- exclude: array, a list of directory and file exclusions. Each
|
||||||
|
exclusion can be either a name or a path. If a file or directory name
|
||||||
|
or path matches the exclusion, it will not be copied. For example,
|
||||||
|
an exclusion of '.svn' will exclude all files and directories whose
|
||||||
|
name is '.svn'. And an exclusion of '/a/b' will exclude file or
|
||||||
|
directory 'sourcePath/a/b'.
|
||||||
|
- translator: the name of the function for translating messages.
|
||||||
|
Defaults to 'Yii::t'. This is used as a mark to find messages to be
|
||||||
|
translated. Accepts both string for single function name or array for
|
||||||
|
multiple function names.
|
||||||
|
- overwrite: if message file must be overwritten with the merged messages.
|
||||||
|
- removeOld: if message no longer needs translation it will be removed,
|
||||||
|
instead of being enclosed between a pair of '@@' marks.
|
||||||
|
- sort: sort messages by key when merging, regardless of their translation
|
||||||
|
state (new, obsolete, translated.)
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
$this->usageError('the configuration file is not specified.');
|
||||||
|
if(!is_file($args[0]))
|
||||||
|
$this->usageError("the configuration file {$args[0]} does not exist.");
|
||||||
|
|
||||||
|
$config=require($args[0]);
|
||||||
|
$translator='Yii::t';
|
||||||
|
extract($config);
|
||||||
|
|
||||||
|
if(!isset($sourcePath,$messagePath,$languages))
|
||||||
|
$this->usageError('The configuration file must specify "sourcePath", "messagePath" and "languages".');
|
||||||
|
if(!is_dir($sourcePath))
|
||||||
|
$this->usageError("The source path $sourcePath is not a valid directory.");
|
||||||
|
if(!is_dir($messagePath))
|
||||||
|
$this->usageError("The message path $messagePath is not a valid directory.");
|
||||||
|
if(empty($languages))
|
||||||
|
$this->usageError("Languages cannot be empty.");
|
||||||
|
|
||||||
|
if(!isset($overwrite))
|
||||||
|
$overwrite = false;
|
||||||
|
|
||||||
|
if(!isset($removeOld))
|
||||||
|
$removeOld = false;
|
||||||
|
|
||||||
|
if(!isset($sort))
|
||||||
|
$sort = false;
|
||||||
|
|
||||||
|
$options=array();
|
||||||
|
if(isset($fileTypes))
|
||||||
|
$options['fileTypes']=$fileTypes;
|
||||||
|
if(isset($exclude))
|
||||||
|
$options['exclude']=$exclude;
|
||||||
|
$files=CFileHelper::findFiles(realpath($sourcePath),$options);
|
||||||
|
|
||||||
|
$messages=array();
|
||||||
|
foreach($files as $file)
|
||||||
|
$messages=array_merge_recursive($messages,$this->extractMessages($file,$translator));
|
||||||
|
|
||||||
|
foreach($languages as $language)
|
||||||
|
{
|
||||||
|
$dir=$messagePath.DIRECTORY_SEPARATOR.$language;
|
||||||
|
if(!is_dir($dir))
|
||||||
|
@mkdir($dir);
|
||||||
|
foreach($messages as $category=>$msgs)
|
||||||
|
{
|
||||||
|
$msgs=array_values(array_unique($msgs));
|
||||||
|
$this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld,$sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function extractMessages($fileName,$translator)
|
||||||
|
{
|
||||||
|
echo "Extracting messages from $fileName...\n";
|
||||||
|
$subject=file_get_contents($fileName);
|
||||||
|
$messages=array();
|
||||||
|
if(!is_array($translator))
|
||||||
|
$translator=array($translator);
|
||||||
|
|
||||||
|
foreach ($translator as $currentTranslator)
|
||||||
|
{
|
||||||
|
$n=preg_match_all('/\b'.$currentTranslator.'\s*\(\s*(\'[\w.\/]*?(?<!\.)\'|"[\w.]*?(?<!\.)")\s*,\s*(\'.*?(?<!\\\\)\'|".*?(?<!\\\\)")\s*[,\)]/s',$subject,$matches,PREG_SET_ORDER);
|
||||||
|
|
||||||
|
for($i=0;$i<$n;++$i)
|
||||||
|
{
|
||||||
|
if(($pos=strpos($matches[$i][1],'.'))!==false)
|
||||||
|
$category=substr($matches[$i][1],$pos+1,-1);
|
||||||
|
else
|
||||||
|
$category=substr($matches[$i][1],1,-1);
|
||||||
|
$message=$matches[$i][2];
|
||||||
|
$messages[$category][]=eval("return $message;"); // use eval to eliminate quote escape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort)
|
||||||
|
{
|
||||||
|
echo "Saving messages to $fileName...";
|
||||||
|
if(is_file($fileName))
|
||||||
|
{
|
||||||
|
$translated=require($fileName);
|
||||||
|
sort($messages);
|
||||||
|
ksort($translated);
|
||||||
|
if(array_keys($translated)==$messages)
|
||||||
|
{
|
||||||
|
echo "nothing new...skipped.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$merged=array();
|
||||||
|
$untranslated=array();
|
||||||
|
foreach($messages as $message)
|
||||||
|
{
|
||||||
|
if(array_key_exists($message,$translated) && strlen($translated[$message])>0)
|
||||||
|
$merged[$message]=$translated[$message];
|
||||||
|
else
|
||||||
|
$untranslated[]=$message;
|
||||||
|
}
|
||||||
|
ksort($merged);
|
||||||
|
sort($untranslated);
|
||||||
|
$todo=array();
|
||||||
|
foreach($untranslated as $message)
|
||||||
|
$todo[$message]='';
|
||||||
|
ksort($translated);
|
||||||
|
foreach($translated as $message=>$translation)
|
||||||
|
{
|
||||||
|
if(!isset($merged[$message]) && !isset($todo[$message]) && !$removeOld)
|
||||||
|
{
|
||||||
|
if(substr($translation,0,2)==='@@' && substr($translation,-2)==='@@')
|
||||||
|
$todo[$message]=$translation;
|
||||||
|
else
|
||||||
|
$todo[$message]='@@'.$translation.'@@';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$merged=array_merge($todo,$merged);
|
||||||
|
if($sort)
|
||||||
|
ksort($merged);
|
||||||
|
if($overwrite === false)
|
||||||
|
$fileName.='.merged';
|
||||||
|
echo "translation merged.\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$merged=array();
|
||||||
|
foreach($messages as $message)
|
||||||
|
$merged[$message]='';
|
||||||
|
ksort($merged);
|
||||||
|
echo "saved.\n";
|
||||||
|
}
|
||||||
|
$array=str_replace("\r",'',var_export($merged,true));
|
||||||
|
$content=<<<EOD
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Message translations.
|
||||||
|
*
|
||||||
|
* This file is automatically generated by 'yiic message' command.
|
||||||
|
* It contains the localizable messages extracted from source code.
|
||||||
|
* You may modify this file by translating the extracted messages.
|
||||||
|
*
|
||||||
|
* Each array element represents the translation (value) of a message (key).
|
||||||
|
* If the value is empty, the message is considered as not translated.
|
||||||
|
* Messages that no longer need translation will have their translations
|
||||||
|
* enclosed between a pair of '@@' marks.
|
||||||
|
*
|
||||||
|
* Message string can be used with plural forms format. Check i18n section
|
||||||
|
* of the guide for details.
|
||||||
|
*
|
||||||
|
* NOTE, this file must be saved in UTF-8 encoding.
|
||||||
|
*/
|
||||||
|
return $array;
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
file_put_contents($fileName, $content);
|
||||||
|
}
|
||||||
|
}
|
585
framework/cli/commands/MigrateCommand.php
Normal file
585
framework/cli/commands/MigrateCommand.php
Normal file
@ -0,0 +1,585 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* MigrateCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MigrateCommand manages the database migrations.
|
||||||
|
*
|
||||||
|
* The implementation of this command and other supporting classes referenced
|
||||||
|
* the yii-dbmigrations extension ((https://github.com/pieterclaerhout/yii-dbmigrations),
|
||||||
|
* authored by Pieter Claerhout.
|
||||||
|
*
|
||||||
|
* Since version 1.1.11 this command will exit with the following exit codes:
|
||||||
|
* <ul>
|
||||||
|
* <li>0 on success</li>
|
||||||
|
* <li>1 on general error</li>
|
||||||
|
* <li>2 on failed migration.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands
|
||||||
|
* @since 1.1.6
|
||||||
|
*/
|
||||||
|
class MigrateCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
const BASE_MIGRATION='m000000_000000_base';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string the directory that stores the migrations. This must be specified
|
||||||
|
* in terms of a path alias, and the corresponding directory must exist.
|
||||||
|
* Defaults to 'application.migrations' (meaning 'protected/migrations').
|
||||||
|
*/
|
||||||
|
public $migrationPath='application.migrations';
|
||||||
|
/**
|
||||||
|
* @var string the name of the table for keeping applied migration information.
|
||||||
|
* This table will be automatically created if not exists. Defaults to 'tbl_migration'.
|
||||||
|
* The table structure is: (version varchar(255) primary key, apply_time integer)
|
||||||
|
*/
|
||||||
|
public $migrationTable='tbl_migration';
|
||||||
|
/**
|
||||||
|
* @var string the application component ID that specifies the database connection for
|
||||||
|
* storing migration information. Defaults to 'db'.
|
||||||
|
*/
|
||||||
|
public $connectionID='db';
|
||||||
|
/**
|
||||||
|
* @var string the path of the template file for generating new migrations. This
|
||||||
|
* must be specified in terms of a path alias (e.g. application.migrations.template).
|
||||||
|
* If not set, an internal template will be used.
|
||||||
|
*/
|
||||||
|
public $templateFile;
|
||||||
|
/**
|
||||||
|
* @var string the default command action. It defaults to 'up'.
|
||||||
|
*/
|
||||||
|
public $defaultAction='up';
|
||||||
|
/**
|
||||||
|
* @var boolean whether to execute the migration in an interactive mode. Defaults to true.
|
||||||
|
* Set this to false when performing migration in a cron job or background process.
|
||||||
|
*/
|
||||||
|
public $interactive=true;
|
||||||
|
|
||||||
|
public function beforeAction($action,$params)
|
||||||
|
{
|
||||||
|
$path=Yii::getPathOfAlias($this->migrationPath);
|
||||||
|
if($path===false || !is_dir($path))
|
||||||
|
{
|
||||||
|
echo 'Error: The migration directory does not exist: '.$this->migrationPath."\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$this->migrationPath=$path;
|
||||||
|
|
||||||
|
$yiiVersion=Yii::getVersion();
|
||||||
|
echo "\nYii Migration Tool v1.0 (based on Yii v{$yiiVersion})\n\n";
|
||||||
|
|
||||||
|
return parent::beforeAction($action,$params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionUp($args)
|
||||||
|
{
|
||||||
|
if(($migrations=$this->getNewMigrations())===array())
|
||||||
|
{
|
||||||
|
echo "No new migration found. Your system is up-to-date.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$total=count($migrations);
|
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 0;
|
||||||
|
if($step>0)
|
||||||
|
$migrations=array_slice($migrations,0,$step);
|
||||||
|
|
||||||
|
$n=count($migrations);
|
||||||
|
if($n===$total)
|
||||||
|
echo "Total $n new ".($n===1 ? 'migration':'migrations')." to be applied:\n";
|
||||||
|
else
|
||||||
|
echo "Total $n out of $total new ".($total===1 ? 'migration':'migrations')." to be applied:\n";
|
||||||
|
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
echo " $migration\n";
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
if($this->confirm('Apply the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||||
|
{
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
{
|
||||||
|
if($this->migrateUp($migration)===false)
|
||||||
|
{
|
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "\nMigrated up successfully.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionDown($args)
|
||||||
|
{
|
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 1;
|
||||||
|
if($step<1)
|
||||||
|
{
|
||||||
|
echo "Error: The step parameter must be greater than 0.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($migrations=$this->getMigrationHistory($step))===array())
|
||||||
|
{
|
||||||
|
echo "No migration has been done before.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$migrations=array_keys($migrations);
|
||||||
|
|
||||||
|
$n=count($migrations);
|
||||||
|
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be reverted:\n";
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
echo " $migration\n";
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
if($this->confirm('Revert the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||||
|
{
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
{
|
||||||
|
if($this->migrateDown($migration)===false)
|
||||||
|
{
|
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "\nMigrated down successfully.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionRedo($args)
|
||||||
|
{
|
||||||
|
$step=isset($args[0]) ? (int)$args[0] : 1;
|
||||||
|
if($step<1)
|
||||||
|
{
|
||||||
|
echo "Error: The step parameter must be greater than 0.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($migrations=$this->getMigrationHistory($step))===array())
|
||||||
|
{
|
||||||
|
echo "No migration has been done before.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$migrations=array_keys($migrations);
|
||||||
|
|
||||||
|
$n=count($migrations);
|
||||||
|
echo "Total $n ".($n===1 ? 'migration':'migrations')." to be redone:\n";
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
echo " $migration\n";
|
||||||
|
echo "\n";
|
||||||
|
|
||||||
|
if($this->confirm('Redo the above '.($n===1 ? 'migration':'migrations')."?"))
|
||||||
|
{
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
{
|
||||||
|
if($this->migrateDown($migration)===false)
|
||||||
|
{
|
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(array_reverse($migrations) as $migration)
|
||||||
|
{
|
||||||
|
if($this->migrateUp($migration)===false)
|
||||||
|
{
|
||||||
|
echo "\nMigration failed. All later migrations are canceled.\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "\nMigration redone successfully.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionTo($args)
|
||||||
|
{
|
||||||
|
if(isset($args[0]))
|
||||||
|
$version=$args[0];
|
||||||
|
else
|
||||||
|
$this->usageError('Please specify which version to migrate to.');
|
||||||
|
|
||||||
|
$originalVersion=$version;
|
||||||
|
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
|
||||||
|
$version='m'.$matches[1];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try migrate up
|
||||||
|
$migrations=$this->getNewMigrations();
|
||||||
|
foreach($migrations as $i=>$migration)
|
||||||
|
{
|
||||||
|
if(strpos($migration,$version.'_')===0)
|
||||||
|
return $this->actionUp(array($i+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// try migrate down
|
||||||
|
$migrations=array_keys($this->getMigrationHistory(-1));
|
||||||
|
foreach($migrations as $i=>$migration)
|
||||||
|
{
|
||||||
|
if(strpos($migration,$version.'_')===0)
|
||||||
|
{
|
||||||
|
if($i===0)
|
||||||
|
{
|
||||||
|
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $this->actionDown(array($i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Error: Unable to find the version '$originalVersion'.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionMark($args)
|
||||||
|
{
|
||||||
|
if(isset($args[0]))
|
||||||
|
$version=$args[0];
|
||||||
|
else
|
||||||
|
$this->usageError('Please specify which version to mark to.');
|
||||||
|
$originalVersion=$version;
|
||||||
|
if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches))
|
||||||
|
$version='m'.$matches[1];
|
||||||
|
else {
|
||||||
|
echo "Error: The version option must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
|
||||||
|
// try mark up
|
||||||
|
$migrations=$this->getNewMigrations();
|
||||||
|
foreach($migrations as $i=>$migration)
|
||||||
|
{
|
||||||
|
if(strpos($migration,$version.'_')===0)
|
||||||
|
{
|
||||||
|
if($this->confirm("Set migration history at $originalVersion?"))
|
||||||
|
{
|
||||||
|
$command=$db->createCommand();
|
||||||
|
for($j=0;$j<=$i;++$j)
|
||||||
|
{
|
||||||
|
$command->insert($this->migrationTable, array(
|
||||||
|
'version'=>$migrations[$j],
|
||||||
|
'apply_time'=>time(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try mark down
|
||||||
|
$migrations=array_keys($this->getMigrationHistory(-1));
|
||||||
|
foreach($migrations as $i=>$migration)
|
||||||
|
{
|
||||||
|
if(strpos($migration,$version.'_')===0)
|
||||||
|
{
|
||||||
|
if($i===0)
|
||||||
|
echo "Already at '$originalVersion'. Nothing needs to be done.\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if($this->confirm("Set migration history at $originalVersion?"))
|
||||||
|
{
|
||||||
|
$command=$db->createCommand();
|
||||||
|
for($j=0;$j<$i;++$j)
|
||||||
|
$command->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$migrations[$j]));
|
||||||
|
echo "The migration history is set at $originalVersion.\nNo actual migration was performed.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Error: Unable to find the version '$originalVersion'.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionHistory($args)
|
||||||
|
{
|
||||||
|
$limit=isset($args[0]) ? (int)$args[0] : -1;
|
||||||
|
$migrations=$this->getMigrationHistory($limit);
|
||||||
|
if($migrations===array())
|
||||||
|
echo "No migration has been done before.\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$n=count($migrations);
|
||||||
|
if($limit>0)
|
||||||
|
echo "Showing the last $n applied ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||||
|
else
|
||||||
|
echo "Total $n ".($n===1 ? 'migration has' : 'migrations have')." been applied before:\n";
|
||||||
|
foreach($migrations as $version=>$time)
|
||||||
|
echo " (".date('Y-m-d H:i:s',$time).') '.$version."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionNew($args)
|
||||||
|
{
|
||||||
|
$limit=isset($args[0]) ? (int)$args[0] : -1;
|
||||||
|
$migrations=$this->getNewMigrations();
|
||||||
|
if($migrations===array())
|
||||||
|
echo "No new migrations found. Your system is up-to-date.\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$n=count($migrations);
|
||||||
|
if($limit>0 && $n>$limit)
|
||||||
|
{
|
||||||
|
$migrations=array_slice($migrations,0,$limit);
|
||||||
|
echo "Showing $limit out of $n new ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Found $n new ".($n===1 ? 'migration' : 'migrations').":\n";
|
||||||
|
|
||||||
|
foreach($migrations as $migration)
|
||||||
|
echo " ".$migration."\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionCreate($args)
|
||||||
|
{
|
||||||
|
if(isset($args[0]))
|
||||||
|
$name=$args[0];
|
||||||
|
else
|
||||||
|
$this->usageError('Please provide the name of the new migration.');
|
||||||
|
|
||||||
|
if(!preg_match('/^\w+$/',$name)) {
|
||||||
|
echo "Error: The name of the migration must contain letters, digits and/or underscore characters only.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name='m'.gmdate('ymd_His').'_'.$name;
|
||||||
|
$content=strtr($this->getTemplate(), array('{ClassName}'=>$name));
|
||||||
|
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$name.'.php';
|
||||||
|
|
||||||
|
if($this->confirm("Create new migration '$file'?"))
|
||||||
|
{
|
||||||
|
file_put_contents($file, $content);
|
||||||
|
echo "New migration created successfully.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm($message,$default=false)
|
||||||
|
{
|
||||||
|
if(!$this->interactive)
|
||||||
|
return true;
|
||||||
|
return parent::confirm($message,$default);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function migrateUp($class)
|
||||||
|
{
|
||||||
|
if($class===self::BASE_MIGRATION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
echo "*** applying $class\n";
|
||||||
|
$start=microtime(true);
|
||||||
|
$migration=$this->instantiateMigration($class);
|
||||||
|
if($migration->up()!==false)
|
||||||
|
{
|
||||||
|
$this->getDbConnection()->createCommand()->insert($this->migrationTable, array(
|
||||||
|
'version'=>$class,
|
||||||
|
'apply_time'=>time(),
|
||||||
|
));
|
||||||
|
$time=microtime(true)-$start;
|
||||||
|
echo "*** applied $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$time=microtime(true)-$start;
|
||||||
|
echo "*** failed to apply $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function migrateDown($class)
|
||||||
|
{
|
||||||
|
if($class===self::BASE_MIGRATION)
|
||||||
|
return;
|
||||||
|
|
||||||
|
echo "*** reverting $class\n";
|
||||||
|
$start=microtime(true);
|
||||||
|
$migration=$this->instantiateMigration($class);
|
||||||
|
if($migration->down()!==false)
|
||||||
|
{
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
$db->createCommand()->delete($this->migrationTable, $db->quoteColumnName('version').'=:version', array(':version'=>$class));
|
||||||
|
$time=microtime(true)-$start;
|
||||||
|
echo "*** reverted $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$time=microtime(true)-$start;
|
||||||
|
echo "*** failed to revert $class (time: ".sprintf("%.3f",$time)."s)\n\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function instantiateMigration($class)
|
||||||
|
{
|
||||||
|
$file=$this->migrationPath.DIRECTORY_SEPARATOR.$class.'.php';
|
||||||
|
require_once($file);
|
||||||
|
$migration=new $class;
|
||||||
|
$migration->setDbConnection($this->getDbConnection());
|
||||||
|
return $migration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CDbConnection
|
||||||
|
*/
|
||||||
|
private $_db;
|
||||||
|
protected function getDbConnection()
|
||||||
|
{
|
||||||
|
if($this->_db!==null)
|
||||||
|
return $this->_db;
|
||||||
|
elseif(($this->_db=Yii::app()->getComponent($this->connectionID)) instanceof CDbConnection)
|
||||||
|
return $this->_db;
|
||||||
|
|
||||||
|
echo "Error: CMigrationCommand.connectionID '{$this->connectionID}' is invalid. Please make sure it refers to the ID of a CDbConnection application component.\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getMigrationHistory($limit)
|
||||||
|
{
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
if($db->schema->getTable($this->migrationTable,true)===null)
|
||||||
|
{
|
||||||
|
$this->createMigrationHistoryTable();
|
||||||
|
}
|
||||||
|
return CHtml::listData($db->createCommand()
|
||||||
|
->select('version, apply_time')
|
||||||
|
->from($this->migrationTable)
|
||||||
|
->order('version DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->queryAll(), 'version', 'apply_time');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createMigrationHistoryTable()
|
||||||
|
{
|
||||||
|
$db=$this->getDbConnection();
|
||||||
|
echo 'Creating migration history table "'.$this->migrationTable.'"...';
|
||||||
|
$db->createCommand()->createTable($this->migrationTable,array(
|
||||||
|
'version'=>'string NOT NULL PRIMARY KEY',
|
||||||
|
'apply_time'=>'integer',
|
||||||
|
));
|
||||||
|
$db->createCommand()->insert($this->migrationTable,array(
|
||||||
|
'version'=>self::BASE_MIGRATION,
|
||||||
|
'apply_time'=>time(),
|
||||||
|
));
|
||||||
|
echo "done.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getNewMigrations()
|
||||||
|
{
|
||||||
|
$applied=array();
|
||||||
|
foreach($this->getMigrationHistory(-1) as $version=>$time)
|
||||||
|
$applied[substr($version,1,13)]=true;
|
||||||
|
|
||||||
|
$migrations=array();
|
||||||
|
$handle=opendir($this->migrationPath);
|
||||||
|
while(($file=readdir($handle))!==false)
|
||||||
|
{
|
||||||
|
if($file==='.' || $file==='..')
|
||||||
|
continue;
|
||||||
|
$path=$this->migrationPath.DIRECTORY_SEPARATOR.$file;
|
||||||
|
if(preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/',$file,$matches) && is_file($path) && !isset($applied[$matches[2]]))
|
||||||
|
$migrations[]=$matches[1];
|
||||||
|
}
|
||||||
|
closedir($handle);
|
||||||
|
sort($migrations);
|
||||||
|
return $migrations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
yiic migrate [action] [parameter]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command provides support for database migrations. The optional
|
||||||
|
'action' parameter specifies which specific migration task to perform.
|
||||||
|
It can take these values: up, down, to, create, history, new, mark.
|
||||||
|
If the 'action' parameter is not given, it defaults to 'up'.
|
||||||
|
Each action takes different parameters. Their usage can be found in
|
||||||
|
the following examples.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
* yiic migrate
|
||||||
|
Applies ALL new migrations. This is equivalent to 'yiic migrate up'.
|
||||||
|
|
||||||
|
* yiic migrate create create_user_table
|
||||||
|
Creates a new migration named 'create_user_table'.
|
||||||
|
|
||||||
|
* yiic migrate up 3
|
||||||
|
Applies the next 3 new migrations.
|
||||||
|
|
||||||
|
* yiic migrate down
|
||||||
|
Reverts the last applied migration.
|
||||||
|
|
||||||
|
* yiic migrate down 3
|
||||||
|
Reverts the last 3 applied migrations.
|
||||||
|
|
||||||
|
* yiic migrate to 101129_185401
|
||||||
|
Migrates up or down to version 101129_185401.
|
||||||
|
|
||||||
|
* yiic migrate mark 101129_185401
|
||||||
|
Modifies the migration history up or down to version 101129_185401.
|
||||||
|
No actual migration will be performed.
|
||||||
|
|
||||||
|
* yiic migrate history
|
||||||
|
Shows all previously applied migration information.
|
||||||
|
|
||||||
|
* yiic migrate history 10
|
||||||
|
Shows the last 10 applied migrations.
|
||||||
|
|
||||||
|
* yiic migrate new
|
||||||
|
Shows all new migrations.
|
||||||
|
|
||||||
|
* yiic migrate new 10
|
||||||
|
Shows the next 10 migrations that have not been applied.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getTemplate()
|
||||||
|
{
|
||||||
|
if($this->templateFile!==null)
|
||||||
|
return file_get_contents(Yii::getPathOfAlias($this->templateFile).'.php');
|
||||||
|
else
|
||||||
|
return <<<EOD
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class {ClassName} extends CDbMigration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
echo "{ClassName} does not support migration down.\\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Use safeUp/safeDown to do migration with transaction
|
||||||
|
public function safeUp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function safeDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
}
|
146
framework/cli/commands/ShellCommand.php
Normal file
146
framework/cli/commands/ShellCommand.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ShellCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShellCommand executes the specified Web application and provides a shell for interaction.
|
||||||
|
*
|
||||||
|
* @property string $help The help information for the shell command.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ShellCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string the help information for the shell command
|
||||||
|
*/
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
yiic shell [entry-script | config-file]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command allows you to interact with a Web application
|
||||||
|
on the command line. It also provides tools to automatically
|
||||||
|
generate new controllers, views and data models.
|
||||||
|
|
||||||
|
It is recommended that you execute this command under
|
||||||
|
the directory that contains the entry script file of
|
||||||
|
the Web application.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* entry-script | config-file: optional, the path to
|
||||||
|
the entry script file or the configuration file for
|
||||||
|
the Web application. If not given, it is assumed to be
|
||||||
|
the 'index.php' file under the current directory.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
$args[0]='index.php';
|
||||||
|
$entryScript=isset($args[0]) ? $args[0] : 'index.php';
|
||||||
|
if(($entryScript=realpath($args[0]))===false || !is_file($entryScript))
|
||||||
|
$this->usageError("{$args[0]} does not exist or is not an entry script file.");
|
||||||
|
|
||||||
|
// fake the web server setting
|
||||||
|
$cwd=getcwd();
|
||||||
|
chdir(dirname($entryScript));
|
||||||
|
$_SERVER['SCRIPT_NAME']='/'.basename($entryScript);
|
||||||
|
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
|
||||||
|
$_SERVER['SCRIPT_FILENAME']=$entryScript;
|
||||||
|
$_SERVER['HTTP_HOST']='localhost';
|
||||||
|
$_SERVER['SERVER_NAME']='localhost';
|
||||||
|
$_SERVER['SERVER_PORT']=80;
|
||||||
|
|
||||||
|
// reset context to run the web application
|
||||||
|
restore_error_handler();
|
||||||
|
restore_exception_handler();
|
||||||
|
Yii::setApplication(null);
|
||||||
|
Yii::setPathOfAlias('application',null);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$config=require($entryScript);
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
// oops, the entry script turns out to be a config file
|
||||||
|
if(is_array($config))
|
||||||
|
{
|
||||||
|
chdir($cwd);
|
||||||
|
$_SERVER['SCRIPT_NAME']='/index.php';
|
||||||
|
$_SERVER['REQUEST_URI']=$_SERVER['SCRIPT_NAME'];
|
||||||
|
$_SERVER['SCRIPT_FILENAME']=$cwd.DIRECTORY_SEPARATOR.'index.php';
|
||||||
|
Yii::createWebApplication($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_error_handler();
|
||||||
|
restore_exception_handler();
|
||||||
|
|
||||||
|
$yiiVersion=Yii::getVersion();
|
||||||
|
echo <<<EOD
|
||||||
|
Yii Interactive Tool v1.1 (based on Yii v{$yiiVersion})
|
||||||
|
Please type 'help' for help. Type 'exit' to quit.
|
||||||
|
EOD;
|
||||||
|
$this->runShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function runShell()
|
||||||
|
{
|
||||||
|
// disable E_NOTICE so that the shell is more friendly
|
||||||
|
error_reporting(E_ALL ^ E_NOTICE);
|
||||||
|
|
||||||
|
$_runner_=new CConsoleCommandRunner;
|
||||||
|
$_runner_->addCommands(dirname(__FILE__).'/shell');
|
||||||
|
$_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell'));
|
||||||
|
if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false)
|
||||||
|
$_runner_->addCommands($_path_);
|
||||||
|
$_commands_=$_runner_->commands;
|
||||||
|
$log=Yii::app()->log;
|
||||||
|
|
||||||
|
while(($_line_=$this->prompt("\n>>"))!==false)
|
||||||
|
{
|
||||||
|
$_line_=trim($_line_);
|
||||||
|
if($_line_==='exit')
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$_args_=preg_split('/[\s,]+/',rtrim($_line_,';'),-1,PREG_SPLIT_NO_EMPTY);
|
||||||
|
if(isset($_args_[0]) && isset($_commands_[$_args_[0]]))
|
||||||
|
{
|
||||||
|
$_command_=$_runner_->createCommand($_args_[0]);
|
||||||
|
array_shift($_args_);
|
||||||
|
$_command_->init();
|
||||||
|
$_command_->run($_args_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo eval($_line_.';');
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
if($e instanceof ShellException)
|
||||||
|
echo $e->getMessage();
|
||||||
|
else
|
||||||
|
echo $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShellException extends CException
|
||||||
|
{
|
||||||
|
}
|
213
framework/cli/commands/WebAppCommand.php
Normal file
213
framework/cli/commands/WebAppCommand.php
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WebAppCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebAppCommand creates an Yii Web application at the specified location.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class WebAppCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
private $_rootPath;
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
yiic webapp <app-path> [<vcs>]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates an Yii Web Application at the specified location.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* app-path: required, the directory where the new application will be created.
|
||||||
|
If the directory does not exist, it will be created. After the application
|
||||||
|
is created, please make sure the directory can be accessed by Web users.
|
||||||
|
* vcs: optional, version control system you're going to use in the new project.
|
||||||
|
Application generator will create all needed files to the specified VCS
|
||||||
|
(such as .gitignore, .gitkeep, etc.). Possible values: git, hg. Do not
|
||||||
|
use this argument if you're going to create VCS files yourself.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
$vcs=false;
|
||||||
|
if(isset($args[1]))
|
||||||
|
{
|
||||||
|
if($args[1]!='git' && $args[1]!='hg')
|
||||||
|
$this->usageError('Unsupported VCS specified. Currently only git and hg supported.');
|
||||||
|
$vcs=$args[1];
|
||||||
|
}
|
||||||
|
if(!isset($args[0]))
|
||||||
|
$this->usageError('the Web application location is not specified.');
|
||||||
|
$path=strtr($args[0],'/\\',DIRECTORY_SEPARATOR);
|
||||||
|
if(strpos($path,DIRECTORY_SEPARATOR)===false)
|
||||||
|
$path='.'.DIRECTORY_SEPARATOR.$path;
|
||||||
|
if(basename($path)=='..')
|
||||||
|
$path.=DIRECTORY_SEPARATOR.'.';
|
||||||
|
$dir=rtrim(realpath(dirname($path)),'\\/');
|
||||||
|
if($dir===false || !is_dir($dir))
|
||||||
|
$this->usageError("The directory '$path' is not valid. Please make sure the parent directory exists.");
|
||||||
|
if(basename($path)==='.')
|
||||||
|
$this->_rootPath=$path=$dir;
|
||||||
|
else
|
||||||
|
$this->_rootPath=$path=$dir.DIRECTORY_SEPARATOR.basename($path);
|
||||||
|
if($this->confirm("Create a Web application under '$path'?"))
|
||||||
|
{
|
||||||
|
$sourceDir=$this->getSourceDir();
|
||||||
|
if($sourceDir===false)
|
||||||
|
die("\nUnable to locate the source directory.\n");
|
||||||
|
$ignoreFiles=array();
|
||||||
|
$renameMap=array();
|
||||||
|
switch($vcs)
|
||||||
|
{
|
||||||
|
case 'git':
|
||||||
|
$renameMap=array('git-gitignore'=>'.gitignore','git-gitkeep'=>'.gitkeep'); // move with rename git files
|
||||||
|
$ignoreFiles=array('hg-hgignore','hg-hgkeep'); // ignore only hg files
|
||||||
|
break;
|
||||||
|
case 'hg':
|
||||||
|
$renameMap=array('hg-hgignore'=>'.hgignore','hg-hgkeep'=>'.hgkeep'); // move with rename hg files
|
||||||
|
$ignoreFiles=array('git-gitignore','git-gitkeep'); // ignore only git files
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no files for renaming
|
||||||
|
$ignoreFiles=array('git-gitignore','git-gitkeep','hg-hgignore','hg-hgkeep'); // ignore both git and hg files
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$list=$this->buildFileList($sourceDir,$path,'',$ignoreFiles,$renameMap);
|
||||||
|
$this->addFileModificationCallbacks($list);
|
||||||
|
$this->copyFiles($list);
|
||||||
|
$this->setPermissions($path);
|
||||||
|
echo "\nYour application has been created successfully under {$path}.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts created application file and directory permissions
|
||||||
|
*
|
||||||
|
* @param string $targetDir path to created application
|
||||||
|
*/
|
||||||
|
protected function setPermissions($targetDir)
|
||||||
|
{
|
||||||
|
@chmod($targetDir.'/assets',0777);
|
||||||
|
@chmod($targetDir.'/protected/runtime',0777);
|
||||||
|
@chmod($targetDir.'/protected/data',0777);
|
||||||
|
@chmod($targetDir.'/protected/data/testdrive.db',0777);
|
||||||
|
@chmod($targetDir.'/protected/yiic',0755);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string path to application bootstrap source files
|
||||||
|
*/
|
||||||
|
protected function getSourceDir()
|
||||||
|
{
|
||||||
|
return realpath(dirname(__FILE__).'/../views/webapp');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds callbacks that will modify source files
|
||||||
|
*
|
||||||
|
* @param array $fileList
|
||||||
|
*/
|
||||||
|
protected function addFileModificationCallbacks(&$fileList)
|
||||||
|
{
|
||||||
|
$fileList['index.php']['callback']=array($this,'generateIndex');
|
||||||
|
$fileList['index-test.php']['callback']=array($this,'generateIndex');
|
||||||
|
$fileList['protected/tests/bootstrap.php']['callback']=array($this,'generateTestBoostrap');
|
||||||
|
$fileList['protected/yiic.php']['callback']=array($this,'generateYiic');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts path to framework's yii.php into application's index.php
|
||||||
|
*
|
||||||
|
* @param string $source source file path
|
||||||
|
* @param array $params
|
||||||
|
* @return string modified source file content
|
||||||
|
*/
|
||||||
|
public function generateIndex($source,$params)
|
||||||
|
{
|
||||||
|
$content=file_get_contents($source);
|
||||||
|
$yii=realpath(dirname(__FILE__).'/../../yii.php');
|
||||||
|
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'index.php');
|
||||||
|
$yii=str_replace('\\','\\\\',$yii);
|
||||||
|
return preg_replace('/\$yii\s*=(.*?);/',"\$yii=$yii;",$content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts path to framework's yiit.php into application's index-test.php
|
||||||
|
*
|
||||||
|
* @param string $source source file path
|
||||||
|
* @param array $params
|
||||||
|
* @return string modified source file content
|
||||||
|
*/
|
||||||
|
public function generateTestBoostrap($source,$params)
|
||||||
|
{
|
||||||
|
$content=file_get_contents($source);
|
||||||
|
$yii=realpath(dirname(__FILE__).'/../../yiit.php');
|
||||||
|
$yii=$this->getRelativePath($yii,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'tests'.DIRECTORY_SEPARATOR.'bootstrap.php');
|
||||||
|
$yii=str_replace('\\','\\\\',$yii);
|
||||||
|
return preg_replace('/\$yiit\s*=(.*?);/',"\$yiit=$yii;",$content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts path to framework's yiic.php into application's yiic.php
|
||||||
|
*
|
||||||
|
* @param string $source source file path
|
||||||
|
* @param array $params
|
||||||
|
* @return string modified source file content
|
||||||
|
*/
|
||||||
|
public function generateYiic($source,$params)
|
||||||
|
{
|
||||||
|
$content=file_get_contents($source);
|
||||||
|
$yiic=realpath(dirname(__FILE__).'/../../yiic.php');
|
||||||
|
$yiic=$this->getRelativePath($yiic,$this->_rootPath.DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'yiic.php');
|
||||||
|
$yiic=str_replace('\\','\\\\',$yiic);
|
||||||
|
return preg_replace('/\$yiic\s*=(.*?);/',"\$yiic=$yiic;",$content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns variant of $path1 relative to $path2
|
||||||
|
*
|
||||||
|
* @param string $path1
|
||||||
|
* @param string $path2
|
||||||
|
* @return string $path1 relative to $path2
|
||||||
|
*/
|
||||||
|
protected function getRelativePath($path1,$path2)
|
||||||
|
{
|
||||||
|
$segs1=explode(DIRECTORY_SEPARATOR,$path1);
|
||||||
|
$segs2=explode(DIRECTORY_SEPARATOR,$path2);
|
||||||
|
$n1=count($segs1);
|
||||||
|
$n2=count($segs2);
|
||||||
|
|
||||||
|
for($i=0;$i<$n1 && $i<$n2;++$i)
|
||||||
|
{
|
||||||
|
if($segs1[$i]!==$segs2[$i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($i===0)
|
||||||
|
return "'".$path1."'";
|
||||||
|
$up='';
|
||||||
|
for($j=$i;$j<$n2-1;++$j)
|
||||||
|
$up.='/..';
|
||||||
|
for(;$i<$n1-1;++$i)
|
||||||
|
$up.='/'.$segs1[$i];
|
||||||
|
|
||||||
|
return 'dirname(__FILE__).\''.$up.'/'.basename($path1).'\'';
|
||||||
|
}
|
||||||
|
}
|
175
framework/cli/commands/shell/ControllerCommand.php
Normal file
175
framework/cli/commands/shell/ControllerCommand.php
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ControllerCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ControllerCommand generates a controller class.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ControllerCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains templates for the model command.
|
||||||
|
* Defaults to null, meaning using 'framework/cli/views/shell/controller'.
|
||||||
|
* If you set this path and some views are missing in the directory,
|
||||||
|
* the default views will be used.
|
||||||
|
*/
|
||||||
|
public $templatePath;
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
controller <controller-ID> [action-ID] ...
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates a controller and views associated with
|
||||||
|
the specified actions.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* controller-ID: required, controller ID, e.g., 'post'.
|
||||||
|
If the controller should be located under a subdirectory,
|
||||||
|
please specify the controller ID as 'path/to/ControllerID',
|
||||||
|
e.g., 'admin/user'.
|
||||||
|
|
||||||
|
If the controller belongs to a module, please specify
|
||||||
|
the controller ID as 'ModuleID/ControllerID' or
|
||||||
|
'ModuleID/path/to/Controller' (assuming the controller is
|
||||||
|
under a subdirectory of that module).
|
||||||
|
|
||||||
|
* action-ID: optional, action ID. You may supply one or several
|
||||||
|
action IDs. A default 'index' action will always be generated.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
* Generates the 'post' controller:
|
||||||
|
controller post
|
||||||
|
|
||||||
|
* Generates the 'post' controller with additional actions 'contact'
|
||||||
|
and 'about':
|
||||||
|
controller post contact about
|
||||||
|
|
||||||
|
* Generates the 'post' controller which should be located under
|
||||||
|
the 'admin' subdirectory of the base controller path:
|
||||||
|
controller admin/post
|
||||||
|
|
||||||
|
* Generates the 'post' controller which should belong to
|
||||||
|
the 'admin' module:
|
||||||
|
controller admin/post
|
||||||
|
|
||||||
|
NOTE: in the last two examples, the commands are the same, but
|
||||||
|
the generated controller file is located under different directories.
|
||||||
|
Yii is able to detect whether 'admin' refers to a module or a subdirectory.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer|null non zero application exit code for help or null on success
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
{
|
||||||
|
echo "Error: controller name is required.\n";
|
||||||
|
echo $this->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$module=Yii::app();
|
||||||
|
$controllerID=$args[0];
|
||||||
|
if(($pos=strrpos($controllerID,'/'))===false)
|
||||||
|
{
|
||||||
|
$controllerClass=ucfirst($controllerID).'Controller';
|
||||||
|
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||||
|
$controllerID[0]=strtolower($controllerID[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$last=substr($controllerID,$pos+1);
|
||||||
|
$last[0]=strtolower($last[0]);
|
||||||
|
$pos2=strpos($controllerID,'/');
|
||||||
|
$first=substr($controllerID,0,$pos2);
|
||||||
|
$middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2);
|
||||||
|
|
||||||
|
$controllerClass=ucfirst($last).'Controller';
|
||||||
|
$controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php';
|
||||||
|
$controllerID=$middle===''?$last:$middle.'/'.$last;
|
||||||
|
if(($m=Yii::app()->getModule($first))!==null)
|
||||||
|
$module=$m;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$controllerFile=$first.'/'.$controllerClass.'.php';
|
||||||
|
$controllerID=$first.'/'.$controllerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$args[]='index';
|
||||||
|
$actions=array_unique(array_splice($args,1));
|
||||||
|
|
||||||
|
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/controller':$this->templatePath;
|
||||||
|
|
||||||
|
$list=array(
|
||||||
|
basename($controllerFile)=>array(
|
||||||
|
'source'=>$templatePath.DIRECTORY_SEPARATOR.'controller.php',
|
||||||
|
'target'=>$controllerFile,
|
||||||
|
'callback'=>array($this,'generateController'),
|
||||||
|
'params'=>array($controllerClass, $actions),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerID);
|
||||||
|
foreach($actions as $name)
|
||||||
|
{
|
||||||
|
$list[$name.'.php']=array(
|
||||||
|
'source'=>$templatePath.DIRECTORY_SEPARATOR.'view.php',
|
||||||
|
'target'=>$viewPath.DIRECTORY_SEPARATOR.$name.'.php',
|
||||||
|
'callback'=>array($this,'generateAction'),
|
||||||
|
'params'=>array('controller'=>$controllerClass, 'action'=>$name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->copyFiles($list);
|
||||||
|
|
||||||
|
if($module instanceof CWebModule)
|
||||||
|
$moduleID=$module->id.'/';
|
||||||
|
else
|
||||||
|
$moduleID='';
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
Controller '{$controllerID}' has been created in the following file:
|
||||||
|
$controllerFile
|
||||||
|
|
||||||
|
You may access it in the browser using the following URL:
|
||||||
|
http://hostname/path/to/index.php?r={$moduleID}{$controllerID}
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateController($source,$params)
|
||||||
|
{
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/controller/'.basename($source);
|
||||||
|
return $this->renderFile($source,array('className'=>$params[0],'actions'=>$params[1]),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateAction($source,$params)
|
||||||
|
{
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/controller/'.basename($source);
|
||||||
|
return $this->renderFile($source,$params,true);
|
||||||
|
}
|
||||||
|
}
|
326
framework/cli/commands/shell/CrudCommand.php
Normal file
326
framework/cli/commands/shell/CrudCommand.php
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CrudCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CrudCommand generates code implementing CRUD operations.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class CrudCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains templates for crud commands.
|
||||||
|
* Defaults to null, meaning using 'framework/cli/views/shell/crud'.
|
||||||
|
* If you set this path and some views are missing in the directory,
|
||||||
|
* the default views will be used.
|
||||||
|
*/
|
||||||
|
public $templatePath;
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains functional test classes.
|
||||||
|
* Defaults to null, meaning using 'protected/tests/functional'.
|
||||||
|
* If this is false, it means functional test file should NOT be generated.
|
||||||
|
*/
|
||||||
|
public $functionalTestPath;
|
||||||
|
/**
|
||||||
|
* @var array list of actions to be created. Each action must be associated with a template file with the same name.
|
||||||
|
*/
|
||||||
|
public $actions=array('create','update','index','view','admin','_form','_view','_search');
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
crud <model-class> [controller-ID] ...
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates a controller and views that accomplish
|
||||||
|
CRUD operations for the specified data model.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* model-class: required, the name of the data model class. This can
|
||||||
|
also be specified as a path alias (e.g. application.models.Post).
|
||||||
|
If the model class belongs to a module, it should be specified
|
||||||
|
as 'ModuleID.models.ClassName'.
|
||||||
|
|
||||||
|
* controller-ID: optional, the controller ID (e.g. 'post').
|
||||||
|
If this is not specified, the model class name will be used
|
||||||
|
as the controller ID. In this case, if the model belongs to
|
||||||
|
a module, the controller will also be created under the same
|
||||||
|
module.
|
||||||
|
|
||||||
|
If the controller should be located under a subdirectory,
|
||||||
|
please specify the controller ID as 'path/to/ControllerID'
|
||||||
|
(e.g. 'admin/user').
|
||||||
|
|
||||||
|
If the controller belongs to a module (different from the module
|
||||||
|
that the model belongs to), please specify the controller ID
|
||||||
|
as 'ModuleID/ControllerID' or 'ModuleID/path/to/Controller'.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
* Generates CRUD for the Post model:
|
||||||
|
crud Post
|
||||||
|
|
||||||
|
* Generates CRUD for the Post model which belongs to module 'admin':
|
||||||
|
crud admin.models.Post
|
||||||
|
|
||||||
|
* Generates CRUD for the Post model. The generated controller should
|
||||||
|
belong to module 'admin', but not the model class:
|
||||||
|
crud Post admin/post
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer|null non zero application exit code for help or null on success
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
{
|
||||||
|
echo "Error: data model class is required.\n";
|
||||||
|
echo $this->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
$module=Yii::app();
|
||||||
|
$modelClass=$args[0];
|
||||||
|
if(($pos=strpos($modelClass,'.'))===false)
|
||||||
|
$modelClass='application.models.'.$modelClass;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$id=substr($modelClass,0,$pos);
|
||||||
|
if(($m=Yii::app()->getModule($id))!==null)
|
||||||
|
$module=$m;
|
||||||
|
}
|
||||||
|
$modelClass=Yii::import($modelClass);
|
||||||
|
|
||||||
|
if(isset($args[1]))
|
||||||
|
{
|
||||||
|
$controllerID=$args[1];
|
||||||
|
if(($pos=strrpos($controllerID,'/'))===false)
|
||||||
|
{
|
||||||
|
$controllerClass=ucfirst($controllerID).'Controller';
|
||||||
|
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||||
|
$controllerID[0]=strtolower($controllerID[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$last=substr($controllerID,$pos+1);
|
||||||
|
$last[0]=strtolower($last);
|
||||||
|
$pos2=strpos($controllerID,'/');
|
||||||
|
$first=substr($controllerID,0,$pos2);
|
||||||
|
$middle=$pos===$pos2?'':substr($controllerID,$pos2+1,$pos-$pos2);
|
||||||
|
|
||||||
|
$controllerClass=ucfirst($last).'Controller';
|
||||||
|
$controllerFile=($middle===''?'':$middle.'/').$controllerClass.'.php';
|
||||||
|
$controllerID=$middle===''?$last:$middle.'/'.$last;
|
||||||
|
if(($m=Yii::app()->getModule($first))!==null)
|
||||||
|
$module=$m;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$controllerFile=$first.'/'.$controllerFile;
|
||||||
|
$controllerID=$first.'/'.$controllerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.str_replace('/',DIRECTORY_SEPARATOR,$controllerFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$controllerID=$modelClass;
|
||||||
|
$controllerClass=ucfirst($controllerID).'Controller';
|
||||||
|
$controllerFile=$module->controllerPath.DIRECTORY_SEPARATOR.$controllerClass.'.php';
|
||||||
|
$controllerID[0]=strtolower($controllerID[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/crud':$this->templatePath;
|
||||||
|
$functionalTestPath=$this->functionalTestPath===null?Yii::getPathOfAlias('application.tests.functional'):$this->functionalTestPath;
|
||||||
|
|
||||||
|
$viewPath=$module->viewPath.DIRECTORY_SEPARATOR.str_replace('.',DIRECTORY_SEPARATOR,$controllerID);
|
||||||
|
$fixtureName=$this->pluralize($modelClass);
|
||||||
|
$fixtureName[0]=strtolower($fixtureName);
|
||||||
|
$list=array(
|
||||||
|
basename($controllerFile)=>array(
|
||||||
|
'source'=>$templatePath.'/controller.php',
|
||||||
|
'target'=>$controllerFile,
|
||||||
|
'callback'=>array($this,'generateController'),
|
||||||
|
'params'=>array($controllerClass,$modelClass),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if($functionalTestPath!==false)
|
||||||
|
{
|
||||||
|
$list[$modelClass.'Test.php']=array(
|
||||||
|
'source'=>$templatePath.'/test.php',
|
||||||
|
'target'=>$functionalTestPath.DIRECTORY_SEPARATOR.$modelClass.'Test.php',
|
||||||
|
'callback'=>array($this,'generateTest'),
|
||||||
|
'params'=>array($controllerID,$fixtureName,$modelClass),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->actions as $action)
|
||||||
|
{
|
||||||
|
$list[$action.'.php']=array(
|
||||||
|
'source'=>$templatePath.'/'.$action.'.php',
|
||||||
|
'target'=>$viewPath.'/'.$action.'.php',
|
||||||
|
'callback'=>array($this,'generateView'),
|
||||||
|
'params'=>$modelClass,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->copyFiles($list);
|
||||||
|
|
||||||
|
if($module instanceof CWebModule)
|
||||||
|
$moduleID=$module->id.'/';
|
||||||
|
else
|
||||||
|
$moduleID='';
|
||||||
|
|
||||||
|
echo "\nCrud '{$controllerID}' has been successfully created. You may access it via:\n";
|
||||||
|
echo "http://hostname/path/to/index.php?r={$moduleID}{$controllerID}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateController($source,$params)
|
||||||
|
{
|
||||||
|
list($controllerClass,$modelClass)=$params;
|
||||||
|
$model=CActiveRecord::model($modelClass);
|
||||||
|
$id=$model->tableSchema->primaryKey;
|
||||||
|
if($id===null)
|
||||||
|
throw new ShellException(Yii::t('yii','Error: Table "{table}" does not have a primary key.',array('{table}'=>$model->tableName())));
|
||||||
|
elseif(is_array($id))
|
||||||
|
throw new ShellException(Yii::t('yii','Error: Table "{table}" has a composite primary key which is not supported by crud command.',array('{table}'=>$model->tableName())));
|
||||||
|
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||||
|
|
||||||
|
return $this->renderFile($source,array(
|
||||||
|
'ID'=>$id,
|
||||||
|
'controllerClass'=>$controllerClass,
|
||||||
|
'modelClass'=>$modelClass,
|
||||||
|
),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateView($source,$modelClass)
|
||||||
|
{
|
||||||
|
$model=CActiveRecord::model($modelClass);
|
||||||
|
$table=$model->getTableSchema();
|
||||||
|
$columns=$table->columns;
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||||
|
return $this->renderFile($source,array(
|
||||||
|
'ID'=>$table->primaryKey,
|
||||||
|
'modelClass'=>$modelClass,
|
||||||
|
'columns'=>$columns),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateTest($source,$params)
|
||||||
|
{
|
||||||
|
list($controllerID,$fixtureName,$modelClass)=$params;
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/crud/'.basename($source);
|
||||||
|
return $this->renderFile($source, array(
|
||||||
|
'controllerID'=>$controllerID,
|
||||||
|
'fixtureName'=>$fixtureName,
|
||||||
|
'modelClass'=>$modelClass,
|
||||||
|
),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateInputLabel($modelClass,$column)
|
||||||
|
{
|
||||||
|
return "CHtml::activeLabelEx(\$model,'{$column->name}')";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateInputField($modelClass,$column)
|
||||||
|
{
|
||||||
|
if($column->type==='boolean')
|
||||||
|
return "CHtml::activeCheckBox(\$model,'{$column->name}')";
|
||||||
|
elseif(stripos($column->dbType,'text')!==false)
|
||||||
|
return "CHtml::activeTextArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
|
||||||
|
$inputField='activePasswordField';
|
||||||
|
else
|
||||||
|
$inputField='activeTextField';
|
||||||
|
|
||||||
|
if($column->type!=='string' || $column->size===null)
|
||||||
|
return "CHtml::{$inputField}(\$model,'{$column->name}')";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($size=$maxLength=$column->size)>60)
|
||||||
|
$size=60;
|
||||||
|
return "CHtml::{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateActiveLabel($modelClass,$column)
|
||||||
|
{
|
||||||
|
return "\$form->labelEx(\$model,'{$column->name}')";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateActiveField($modelClass,$column)
|
||||||
|
{
|
||||||
|
if($column->type==='boolean')
|
||||||
|
return "\$form->checkBox(\$model,'{$column->name}')";
|
||||||
|
elseif(stripos($column->dbType,'text')!==false)
|
||||||
|
return "\$form->textArea(\$model,'{$column->name}',array('rows'=>6, 'cols'=>50))";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(preg_match('/^(password|pass|passwd|passcode)$/i',$column->name))
|
||||||
|
$inputField='passwordField';
|
||||||
|
else
|
||||||
|
$inputField='textField';
|
||||||
|
|
||||||
|
if($column->type!=='string' || $column->size===null)
|
||||||
|
return "\$form->{$inputField}(\$model,'{$column->name}')";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(($size=$maxLength=$column->size)>60)
|
||||||
|
$size=60;
|
||||||
|
return "\$form->{$inputField}(\$model,'{$column->name}',array('size'=>$size,'maxlength'=>$maxLength))";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function guessNameColumn($columns)
|
||||||
|
{
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if(!strcasecmp($column->name,'name'))
|
||||||
|
return $column->name;
|
||||||
|
}
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if(!strcasecmp($column->name,'title'))
|
||||||
|
return $column->name;
|
||||||
|
}
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if($column->isPrimaryKey)
|
||||||
|
return $column->name;
|
||||||
|
}
|
||||||
|
return 'id';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function class2id($className)
|
||||||
|
{
|
||||||
|
return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function class2name($className,$pluralize=false)
|
||||||
|
{
|
||||||
|
if($pluralize)
|
||||||
|
$className=$this->pluralize($className);
|
||||||
|
return ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $className)))));
|
||||||
|
}
|
||||||
|
}
|
122
framework/cli/commands/shell/FormCommand.php
Normal file
122
framework/cli/commands/shell/FormCommand.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* FormCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FormCommand generates a form view based on a specified model.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class FormCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains templates for the form command.
|
||||||
|
* Defaults to null, meaning using 'framework/cli/views/shell/form'.
|
||||||
|
* If you set this path and some views are missing in the directory,
|
||||||
|
* the default views will be used.
|
||||||
|
*/
|
||||||
|
public $templatePath;
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
form <model-class> <view-name> [scenario]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates a form view that can be used to collect inputs
|
||||||
|
for the specified model.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* model-class: required, model class. This can be either the name of
|
||||||
|
the model class (e.g. 'ContactForm') or the path alias of the model
|
||||||
|
class file (e.g. 'application.models.ContactForm'). The former can
|
||||||
|
be used only if the class can be autoloaded.
|
||||||
|
|
||||||
|
* view-name: required, the name of the view to be generated. This should
|
||||||
|
be the path alias of the view script (e.g. 'application.views.site.contact').
|
||||||
|
|
||||||
|
* scenario: optional, the name of the scenario in which the model is used
|
||||||
|
(e.g. 'update', 'login'). This determines which model attributes the
|
||||||
|
generated form view will be used to collect user inputs for. If this
|
||||||
|
is not provided, the scenario will be assumed to be '' (empty string).
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
* Generates the view script for the 'ContactForm' model:
|
||||||
|
form ContactForm application.views.site.contact
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer|null non zero application exit code for help or null on success
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0],$args[1]))
|
||||||
|
{
|
||||||
|
echo "Error: both model class and view name are required.\n";
|
||||||
|
echo $this->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
$scenario=isset($args[2]) ? $args[2] : '';
|
||||||
|
$modelClass=Yii::import($args[0],true);
|
||||||
|
$model=new $modelClass($scenario);
|
||||||
|
$attributes=$model->getSafeAttributeNames();
|
||||||
|
|
||||||
|
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/form':$this->templatePath;
|
||||||
|
$viewPath=Yii::getPathOfAlias($args[1]);
|
||||||
|
$viewName=basename($viewPath);
|
||||||
|
$viewPath.='.php';
|
||||||
|
$params=array(
|
||||||
|
'modelClass'=>$modelClass,
|
||||||
|
'viewName'=>$viewName,
|
||||||
|
'attributes'=>$attributes,
|
||||||
|
);
|
||||||
|
$list=array(
|
||||||
|
basename($viewPath)=>array(
|
||||||
|
'source'=>$templatePath.'/form.php',
|
||||||
|
'target'=>$viewPath,
|
||||||
|
'callback'=>array($this,'generateForm'),
|
||||||
|
'params'=>$params,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->copyFiles($list);
|
||||||
|
|
||||||
|
$actionFile=$templatePath.'/action.php';
|
||||||
|
if(!is_file($actionFile)) // fall back to default ones
|
||||||
|
$actionFile=YII_PATH.'/cli/views/shell/form/action.php';
|
||||||
|
|
||||||
|
echo "The following form view has been successfully created:\n";
|
||||||
|
echo "\t$viewPath\n\n";
|
||||||
|
echo "You may use the following code in your controller action:\n\n";
|
||||||
|
echo $this->renderFile($actionFile,$params,true);
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateForm($source,$params)
|
||||||
|
{
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/form/'.basename($source);
|
||||||
|
|
||||||
|
return $this->renderFile($source,$params,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function class2id($className)
|
||||||
|
{
|
||||||
|
if(strrpos($className,'Form')===strlen($className)-4)
|
||||||
|
$className=substr($className,0,strlen($className)-4);
|
||||||
|
return trim(strtolower(str_replace('_','-',preg_replace('/(?<![A-Z])[A-Z]/', '-\0', $className))),'-');
|
||||||
|
}
|
||||||
|
}
|
78
framework/cli/commands/shell/HelpCommand.php
Normal file
78
framework/cli/commands/shell/HelpCommand.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HelpCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HelpCommand displays help information for commands under yiic shell.
|
||||||
|
*
|
||||||
|
* @property string $help The command description.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class HelpCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer non zero application exit code for help
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
$runner=$this->getCommandRunner();
|
||||||
|
$commands=$runner->commands;
|
||||||
|
if(isset($args[0]))
|
||||||
|
$name=strtolower($args[0]);
|
||||||
|
if(!isset($args[0]) || !isset($commands[$name]))
|
||||||
|
{
|
||||||
|
echo <<<EOD
|
||||||
|
At the prompt, you may enter a PHP statement or one of the following commands:
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
$commandNames=array_keys($commands);
|
||||||
|
sort($commandNames);
|
||||||
|
echo ' - '.implode("\n - ",$commandNames);
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
|
||||||
|
Type 'help <command-name>' for details about a command.
|
||||||
|
|
||||||
|
To expand the above command list, place your command class files
|
||||||
|
under 'protected/commands/shell', or a directory specified
|
||||||
|
by the 'YIIC_SHELL_COMMAND_PATH' environment variable. The command class
|
||||||
|
must extend from CConsoleCommand.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo $runner->createCommand($name)->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the command description.
|
||||||
|
* @return string the command description.
|
||||||
|
*/
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
help [command-name]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Display the help information for the specified command.
|
||||||
|
If the command name is not given, all commands will be listed.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* command-name: optional, the name of the command to show help information.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
}
|
488
framework/cli/commands/shell/ModelCommand.php
Normal file
488
framework/cli/commands/shell/ModelCommand.php
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ModelCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModelCommand generates a model class.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
class ModelCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains templates for the model command.
|
||||||
|
* Defaults to null, meaning using 'framework/cli/views/shell/model'.
|
||||||
|
* If you set this path and some views are missing in the directory,
|
||||||
|
* the default views will be used.
|
||||||
|
*/
|
||||||
|
public $templatePath;
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains test fixtures.
|
||||||
|
* Defaults to null, meaning using 'protected/tests/fixtures'.
|
||||||
|
* If this is false, it means fixture file should NOT be generated.
|
||||||
|
*/
|
||||||
|
public $fixturePath;
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains unit test classes.
|
||||||
|
* Defaults to null, meaning using 'protected/tests/unit'.
|
||||||
|
* If this is false, it means unit test file should NOT be generated.
|
||||||
|
*/
|
||||||
|
public $unitTestPath;
|
||||||
|
|
||||||
|
private $_schema;
|
||||||
|
private $_relations; // where we keep table relations
|
||||||
|
private $_tables;
|
||||||
|
private $_classes;
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
model <class-name> [table-name]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates a model class with the specified class name.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* class-name: required, model class name. By default, the generated
|
||||||
|
model class file will be placed under the directory aliased as
|
||||||
|
'application.models'. To override this default, specify the class
|
||||||
|
name in terms of a path alias, e.g., 'application.somewhere.ClassName'.
|
||||||
|
|
||||||
|
If the model class belongs to a module, it should be specified
|
||||||
|
as 'ModuleID.models.ClassName'.
|
||||||
|
|
||||||
|
If the class name ends with '*', then a model class will be generated
|
||||||
|
for EVERY table in the database.
|
||||||
|
|
||||||
|
If the class name contains a regular expression deliminated by slashes,
|
||||||
|
then a model class will be generated for those tables whose name
|
||||||
|
matches the regular expression. If the regular expression contains
|
||||||
|
sub-patterns, the first sub-pattern will be used to generate the model
|
||||||
|
class name.
|
||||||
|
|
||||||
|
* table-name: optional, the associated database table name. If not given,
|
||||||
|
it is assumed to be the model class name.
|
||||||
|
|
||||||
|
Note, when the class name ends with '*', this parameter will be
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
* Generates the Post model:
|
||||||
|
model Post
|
||||||
|
|
||||||
|
* Generates the Post model which is associated with table 'posts':
|
||||||
|
model Post posts
|
||||||
|
|
||||||
|
* Generates the Post model which should belong to module 'admin':
|
||||||
|
model admin.models.Post
|
||||||
|
|
||||||
|
* Generates a model class for every table in the current database:
|
||||||
|
model *
|
||||||
|
|
||||||
|
* Same as above, but the model class files should be generated
|
||||||
|
under 'protected/models2':
|
||||||
|
model application.models2.*
|
||||||
|
|
||||||
|
* Generates a model class for every table whose name is prefixed
|
||||||
|
with 'tbl_' in the current database. The model class will not
|
||||||
|
contain the table prefix.
|
||||||
|
model /^tbl_(.*)$/
|
||||||
|
|
||||||
|
* Same as above, but the model class files should be generated
|
||||||
|
under 'protected/models2':
|
||||||
|
model application.models2./^tbl_(.*)$/
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given table is a "many to many" helper table.
|
||||||
|
* Their PK has 2 fields, and both of those fields are also FK to other separate tables.
|
||||||
|
* @param CDbTableSchema $table table to inspect
|
||||||
|
* @return boolean true if table matches description of helper table.
|
||||||
|
*/
|
||||||
|
protected function isRelationTable($table)
|
||||||
|
{
|
||||||
|
$pk=$table->primaryKey;
|
||||||
|
return (count($pk) === 2 // we want 2 columns
|
||||||
|
&& isset($table->foreignKeys[$pk[0]]) // pk column 1 is also a foreign key
|
||||||
|
&& isset($table->foreignKeys[$pk[1]]) // pk column 2 is also a foreign key
|
||||||
|
&& $table->foreignKeys[$pk[0]][0] !== $table->foreignKeys[$pk[1]][0]); // and the foreign keys point different tables
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate code to put in ActiveRecord class's relations() function.
|
||||||
|
* @return array indexed by table names, each entry contains array of php code to go in appropriate ActiveRecord class.
|
||||||
|
* Empty array is returned if database couldn't be connected.
|
||||||
|
*/
|
||||||
|
protected function generateRelations()
|
||||||
|
{
|
||||||
|
$this->_relations=array();
|
||||||
|
$this->_classes=array();
|
||||||
|
foreach($this->_schema->getTables() as $table)
|
||||||
|
{
|
||||||
|
$tableName=$table->name;
|
||||||
|
|
||||||
|
if ($this->isRelationTable($table))
|
||||||
|
{
|
||||||
|
$pks=$table->primaryKey;
|
||||||
|
$fks=$table->foreignKeys;
|
||||||
|
|
||||||
|
$table0=$fks[$pks[1]][0];
|
||||||
|
$table1=$fks[$pks[0]][0];
|
||||||
|
$className0=$this->getClassName($table0);
|
||||||
|
$className1=$this->getClassName($table1);
|
||||||
|
|
||||||
|
$unprefixedTableName=$this->removePrefix($tableName,true);
|
||||||
|
|
||||||
|
$relationName=$this->generateRelationName($table0, $table1, true);
|
||||||
|
$this->_relations[$className0][$relationName]="array(self::MANY_MANY, '$className1', '$unprefixedTableName($pks[0], $pks[1])')";
|
||||||
|
|
||||||
|
$relationName=$this->generateRelationName($table1, $table0, true);
|
||||||
|
$this->_relations[$className1][$relationName]="array(self::MANY_MANY, '$className0', '$unprefixedTableName($pks[0], $pks[1])')";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->_classes[$tableName]=$className=$this->getClassName($tableName);
|
||||||
|
foreach ($table->foreignKeys as $fkName => $fkEntry)
|
||||||
|
{
|
||||||
|
// Put table and key name in variables for easier reading
|
||||||
|
$refTable=$fkEntry[0]; // Table name that current fk references to
|
||||||
|
$refKey=$fkEntry[1]; // Key in that table being referenced
|
||||||
|
$refClassName=$this->getClassName($refTable);
|
||||||
|
|
||||||
|
// Add relation for this table
|
||||||
|
$relationName=$this->generateRelationName($tableName, $fkName, false);
|
||||||
|
$this->_relations[$className][$relationName]="array(self::BELONGS_TO, '$refClassName', '$fkName')";
|
||||||
|
|
||||||
|
// Add relation for the referenced table
|
||||||
|
$relationType=$table->primaryKey === $fkName ? 'HAS_ONE' : 'HAS_MANY';
|
||||||
|
$relationName=$this->generateRelationName($refTable, $this->removePrefix($tableName), $relationType==='HAS_MANY');
|
||||||
|
$this->_relations[$refClassName][$relationName]="array(self::$relationType, '$className', '$fkName')";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getClassName($tableName)
|
||||||
|
{
|
||||||
|
return isset($this->_tables[$tableName]) ? $this->_tables[$tableName] : $this->generateClassName($tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates model class name based on a table name
|
||||||
|
* @param string $tableName the table name
|
||||||
|
* @return string the generated model class name
|
||||||
|
*/
|
||||||
|
protected function generateClassName($tableName)
|
||||||
|
{
|
||||||
|
return str_replace(' ','',
|
||||||
|
ucwords(
|
||||||
|
trim(
|
||||||
|
strtolower(
|
||||||
|
str_replace(array('-','_'),' ',
|
||||||
|
preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $tableName))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the mapping table between table names and class names.
|
||||||
|
* @param CDbSchema $schema the database schema
|
||||||
|
* @param string $pattern a regular expression that may be used to filter table names
|
||||||
|
*/
|
||||||
|
protected function generateClassNames($schema,$pattern=null)
|
||||||
|
{
|
||||||
|
$this->_tables=array();
|
||||||
|
foreach($schema->getTableNames() as $name)
|
||||||
|
{
|
||||||
|
if($pattern===null)
|
||||||
|
$this->_tables[$name]=$this->generateClassName($this->removePrefix($name));
|
||||||
|
elseif(preg_match($pattern,$name,$matches))
|
||||||
|
{
|
||||||
|
if(count($matches)>1 && !empty($matches[1]))
|
||||||
|
$className=$this->generateClassName($matches[1]);
|
||||||
|
else
|
||||||
|
$className=$this->generateClassName($matches[0]);
|
||||||
|
$this->_tables[$name]=empty($className) ? $name : $className;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a name for use as a relation name (inside relations() function in a model).
|
||||||
|
* @param string $tableName the name of the table to hold the relation
|
||||||
|
* @param string $fkName the foreign key name
|
||||||
|
* @param boolean $multiple whether the relation would contain multiple objects
|
||||||
|
* @return string the generated relation name
|
||||||
|
*/
|
||||||
|
protected function generateRelationName($tableName, $fkName, $multiple)
|
||||||
|
{
|
||||||
|
if(strcasecmp(substr($fkName,-2),'id')===0 && strcasecmp($fkName,'id'))
|
||||||
|
$relationName=rtrim(substr($fkName, 0, -2),'_');
|
||||||
|
else
|
||||||
|
$relationName=$fkName;
|
||||||
|
$relationName[0]=strtolower($relationName);
|
||||||
|
|
||||||
|
$rawName=$relationName;
|
||||||
|
if($multiple)
|
||||||
|
$relationName=$this->pluralize($relationName);
|
||||||
|
|
||||||
|
$table=$this->_schema->getTable($tableName);
|
||||||
|
$i=0;
|
||||||
|
while(isset($table->columns[$relationName]))
|
||||||
|
$relationName=$rawName.($i++);
|
||||||
|
return $relationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer|null non zero application exit code for help or null on success
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
{
|
||||||
|
echo "Error: model class name is required.\n";
|
||||||
|
echo $this->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
$className=$args[0];
|
||||||
|
|
||||||
|
if(($db=Yii::app()->getDb())===null)
|
||||||
|
{
|
||||||
|
echo "Error: an active 'db' connection is required.\n";
|
||||||
|
echo "If you already added 'db' component in application configuration,\n";
|
||||||
|
echo "please quit and re-enter the yiic shell.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->active=true;
|
||||||
|
$this->_schema=$db->schema;
|
||||||
|
|
||||||
|
if(!preg_match('/^[\w\.\-\*]*(.*?)$/',$className,$matches))
|
||||||
|
{
|
||||||
|
echo "Error: model class name is invalid.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($matches[1])) // without regular expression
|
||||||
|
{
|
||||||
|
$this->generateClassNames($this->_schema);
|
||||||
|
if(($pos=strrpos($className,'.'))===false)
|
||||||
|
$basePath=Yii::getPathOfAlias('application.models');
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$basePath=Yii::getPathOfAlias(substr($className,0,$pos));
|
||||||
|
$className=substr($className,$pos+1);
|
||||||
|
}
|
||||||
|
if($className==='*') // generate all models
|
||||||
|
$this->generateRelations();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$tableName=isset($args[1])?$args[1]:$className;
|
||||||
|
$tableName=$this->addPrefix($tableName);
|
||||||
|
$this->_tables[$tableName]=$className;
|
||||||
|
$this->generateRelations();
|
||||||
|
$this->_classes=array($tableName=>$className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // with regular expression
|
||||||
|
{
|
||||||
|
$pattern=$matches[1];
|
||||||
|
$pos=strrpos($className,$pattern);
|
||||||
|
if($pos>0) // only regexp is given
|
||||||
|
$basePath=Yii::getPathOfAlias(rtrim(substr($className,0,$pos),'.'));
|
||||||
|
else
|
||||||
|
$basePath=Yii::getPathOfAlias('application.models');
|
||||||
|
$this->generateClassNames($this->_schema,$pattern);
|
||||||
|
$classes=$this->_tables;
|
||||||
|
$this->generateRelations();
|
||||||
|
$this->_classes=$classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($this->_classes)>1)
|
||||||
|
{
|
||||||
|
$entries=array();
|
||||||
|
$count=0;
|
||||||
|
foreach($this->_classes as $tableName=>$className)
|
||||||
|
$entries[]=++$count.". $className ($tableName)";
|
||||||
|
echo "The following model classes (tables) match your criteria:\n";
|
||||||
|
echo implode("\n",$entries)."\n\n";
|
||||||
|
if(!$this->confirm("Do you want to generate the above classes?"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$templatePath=$this->templatePath===null?YII_PATH.'/cli/views/shell/model':$this->templatePath;
|
||||||
|
$fixturePath=$this->fixturePath===null?Yii::getPathOfAlias('application.tests.fixtures'):$this->fixturePath;
|
||||||
|
$unitTestPath=$this->unitTestPath===null?Yii::getPathOfAlias('application.tests.unit'):$this->unitTestPath;
|
||||||
|
|
||||||
|
$list=array();
|
||||||
|
$files=array();
|
||||||
|
foreach ($this->_classes as $tableName=>$className)
|
||||||
|
{
|
||||||
|
$files[$className]=$classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php';
|
||||||
|
$list['models/'.$className.'.php']=array(
|
||||||
|
'source'=>$templatePath.DIRECTORY_SEPARATOR.'model.php',
|
||||||
|
'target'=>$classFile,
|
||||||
|
'callback'=>array($this,'generateModel'),
|
||||||
|
'params'=>array($className,$tableName),
|
||||||
|
);
|
||||||
|
if($fixturePath!==false)
|
||||||
|
{
|
||||||
|
$list['fixtures/'.$tableName.'.php']=array(
|
||||||
|
'source'=>$templatePath.DIRECTORY_SEPARATOR.'fixture.php',
|
||||||
|
'target'=>$fixturePath.DIRECTORY_SEPARATOR.$tableName.'.php',
|
||||||
|
'callback'=>array($this,'generateFixture'),
|
||||||
|
'params'=>$this->_schema->getTable($tableName),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if($unitTestPath!==false)
|
||||||
|
{
|
||||||
|
$fixtureName=$this->pluralize($className);
|
||||||
|
$fixtureName[0]=strtolower($fixtureName);
|
||||||
|
$list['unit/'.$className.'Test.php']=array(
|
||||||
|
'source'=>$templatePath.DIRECTORY_SEPARATOR.'test.php',
|
||||||
|
'target'=>$unitTestPath.DIRECTORY_SEPARATOR.$className.'Test.php',
|
||||||
|
'callback'=>array($this,'generateTest'),
|
||||||
|
'params'=>array($className,$fixtureName),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->copyFiles($list);
|
||||||
|
|
||||||
|
foreach($files as $className=>$file)
|
||||||
|
{
|
||||||
|
if(!class_exists($className,false))
|
||||||
|
include_once($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
$classes=implode(", ", $this->_classes);
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
The following model classes are successfully generated:
|
||||||
|
$classes
|
||||||
|
|
||||||
|
If you have a 'db' database connection, you can test these models now with:
|
||||||
|
\$model={$className}::model()->find();
|
||||||
|
print_r(\$model);
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateModel($source,$params)
|
||||||
|
{
|
||||||
|
list($className,$tableName)=$params;
|
||||||
|
$rules=array();
|
||||||
|
$labels=array();
|
||||||
|
$relations=array();
|
||||||
|
if(($table=$this->_schema->getTable($tableName))!==null)
|
||||||
|
{
|
||||||
|
$required=array();
|
||||||
|
$integers=array();
|
||||||
|
$numerical=array();
|
||||||
|
$length=array();
|
||||||
|
$safe=array();
|
||||||
|
foreach($table->columns as $column)
|
||||||
|
{
|
||||||
|
$label=ucwords(trim(strtolower(str_replace(array('-','_'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $column->name)))));
|
||||||
|
$label=preg_replace('/\s+/',' ',$label);
|
||||||
|
if(strcasecmp(substr($label,-3),' id')===0)
|
||||||
|
$label=substr($label,0,-3);
|
||||||
|
$labels[$column->name]=$label;
|
||||||
|
if($column->isPrimaryKey && $table->sequenceName!==null)
|
||||||
|
continue;
|
||||||
|
$r=!$column->allowNull && $column->defaultValue===null;
|
||||||
|
if($r)
|
||||||
|
$required[]=$column->name;
|
||||||
|
if($column->type==='integer')
|
||||||
|
$integers[]=$column->name;
|
||||||
|
elseif($column->type==='double')
|
||||||
|
$numerical[]=$column->name;
|
||||||
|
elseif($column->type==='string' && $column->size>0)
|
||||||
|
$length[$column->size][]=$column->name;
|
||||||
|
elseif(!$column->isPrimaryKey && !$r)
|
||||||
|
$safe[]=$column->name;
|
||||||
|
}
|
||||||
|
if($required!==array())
|
||||||
|
$rules[]="array('".implode(', ',$required)."', 'required')";
|
||||||
|
if($integers!==array())
|
||||||
|
$rules[]="array('".implode(', ',$integers)."', 'numerical', 'integerOnly'=>true)";
|
||||||
|
if($numerical!==array())
|
||||||
|
$rules[]="array('".implode(', ',$numerical)."', 'numerical')";
|
||||||
|
if($length!==array())
|
||||||
|
{
|
||||||
|
foreach($length as $len=>$cols)
|
||||||
|
$rules[]="array('".implode(', ',$cols)."', 'length', 'max'=>$len)";
|
||||||
|
}
|
||||||
|
if($safe!==array())
|
||||||
|
$rules[]="array('".implode(', ',$safe)."', 'safe')";
|
||||||
|
|
||||||
|
if(isset($this->_relations[$className]) && is_array($this->_relations[$className]))
|
||||||
|
$relations=$this->_relations[$className];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
echo "Warning: the table '$tableName' does not exist in the database.\n";
|
||||||
|
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||||
|
return $this->renderFile($source,array(
|
||||||
|
'className'=>$className,
|
||||||
|
'tableName'=>$this->removePrefix($tableName,true),
|
||||||
|
'columns'=>isset($table) ? $table->columns : array(),
|
||||||
|
'rules'=>$rules,
|
||||||
|
'labels'=>$labels,
|
||||||
|
'relations'=>$relations,
|
||||||
|
),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateFixture($source,$table)
|
||||||
|
{
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||||
|
return $this->renderFile($source, array(
|
||||||
|
'table'=>$table,
|
||||||
|
),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateTest($source,$params)
|
||||||
|
{
|
||||||
|
list($className,$fixtureName)=$params;
|
||||||
|
if(!is_file($source)) // fall back to default ones
|
||||||
|
$source=YII_PATH.'/cli/views/shell/model/'.basename($source);
|
||||||
|
return $this->renderFile($source, array(
|
||||||
|
'className'=>$className,
|
||||||
|
'fixtureName'=>$fixtureName,
|
||||||
|
),true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function removePrefix($tableName,$addBrackets=false)
|
||||||
|
{
|
||||||
|
$tablePrefix=Yii::app()->getDb()->tablePrefix;
|
||||||
|
if($tablePrefix!='' && !strncmp($tableName,$tablePrefix,strlen($tablePrefix)))
|
||||||
|
{
|
||||||
|
$tableName=substr($tableName,strlen($tablePrefix));
|
||||||
|
if($addBrackets)
|
||||||
|
$tableName='{{'.$tableName.'}}';
|
||||||
|
}
|
||||||
|
return $tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function addPrefix($tableName)
|
||||||
|
{
|
||||||
|
$tablePrefix=Yii::app()->getDb()->tablePrefix;
|
||||||
|
if($tablePrefix!='' && strncmp($tableName,$tablePrefix,strlen($tablePrefix)))
|
||||||
|
$tableName=$tablePrefix.$tableName;
|
||||||
|
return $tableName;
|
||||||
|
}
|
||||||
|
}
|
93
framework/cli/commands/shell/ModuleCommand.php
Normal file
93
framework/cli/commands/shell/ModuleCommand.php
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* ModuleCommand class file.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2013 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
* @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ModuleCommand generates a controller class.
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @version $Id: ModuleCommand.php 433 2008-12-30 22:59:17Z qiang.xue $
|
||||||
|
* @package system.cli.commands.shell
|
||||||
|
*/
|
||||||
|
class ModuleCommand extends CConsoleCommand
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the directory that contains templates for the module command.
|
||||||
|
* Defaults to null, meaning using 'framework/cli/views/shell/module'.
|
||||||
|
* If you set this path and some views are missing in the directory,
|
||||||
|
* the default views will be used.
|
||||||
|
*/
|
||||||
|
public $templatePath;
|
||||||
|
|
||||||
|
public function getHelp()
|
||||||
|
{
|
||||||
|
return <<<EOD
|
||||||
|
USAGE
|
||||||
|
module <module-ID>
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
This command generates an application module.
|
||||||
|
|
||||||
|
PARAMETERS
|
||||||
|
* module-ID: required, module ID. It is case-sensitive.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the action.
|
||||||
|
* @param array $args command line parameters specific for this command
|
||||||
|
* @return integer|null non zero application exit code for help or null on success
|
||||||
|
*/
|
||||||
|
public function run($args)
|
||||||
|
{
|
||||||
|
if(!isset($args[0]))
|
||||||
|
{
|
||||||
|
echo "Error: module ID is required.\n";
|
||||||
|
echo $this->getHelp();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$moduleID=$args[0];
|
||||||
|
$moduleClass=ucfirst($moduleID).'Module';
|
||||||
|
$modulePath=Yii::app()->getModulePath().DIRECTORY_SEPARATOR.$moduleID;
|
||||||
|
|
||||||
|
$sourceDir=$this->templatePath===null?YII_PATH.'/cli/views/shell/module':$this->templatePath;
|
||||||
|
$list=$this->buildFileList($sourceDir,$modulePath);
|
||||||
|
$list['module.php']['target']=$modulePath.DIRECTORY_SEPARATOR.$moduleClass.'.php';
|
||||||
|
$list['module.php']['callback']=array($this,'generateModuleClass');
|
||||||
|
$list['module.php']['params']=array(
|
||||||
|
'moduleClass'=>$moduleClass,
|
||||||
|
'moduleID'=>$moduleID,
|
||||||
|
);
|
||||||
|
$list[$moduleClass.'.php']=$list['module.php'];
|
||||||
|
unset($list['module.php']);
|
||||||
|
|
||||||
|
$this->copyFiles($list);
|
||||||
|
|
||||||
|
echo <<<EOD
|
||||||
|
|
||||||
|
Module '{$moduleID}' has been created under the following folder:
|
||||||
|
$modulePath
|
||||||
|
|
||||||
|
You may access it in the browser using the following URL:
|
||||||
|
http://hostname/path/to/index.php?r=$moduleID
|
||||||
|
|
||||||
|
Note, the module needs to be installed first by adding '{$moduleID}'
|
||||||
|
to the 'modules' property in the application configuration.
|
||||||
|
|
||||||
|
EOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateModuleClass($source,$params)
|
||||||
|
{
|
||||||
|
return $this->renderFile($source,$params,true);
|
||||||
|
}
|
||||||
|
}
|
47
framework/cli/views/shell/controller/controller.php
Normal file
47
framework/cli/views/shell/controller/controller.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating a controller class file.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $className: the class name of the controller
|
||||||
|
* - $actions: a list of action names for the controller
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
class <?php echo $className; ?> extends Controller
|
||||||
|
{
|
||||||
|
<?php foreach($actions as $action): ?>
|
||||||
|
public function action<?php echo ucfirst($action); ?>()
|
||||||
|
{
|
||||||
|
$this->render('<?php echo $action; ?>');
|
||||||
|
}
|
||||||
|
|
||||||
|
<?php endforeach; ?>
|
||||||
|
// -----------------------------------------------------------
|
||||||
|
// Uncomment the following methods and override them if needed
|
||||||
|
/*
|
||||||
|
public function filters()
|
||||||
|
{
|
||||||
|
// return the filter configuration for this controller, e.g.:
|
||||||
|
return array(
|
||||||
|
'inlineFilterName',
|
||||||
|
array(
|
||||||
|
'class'=>'path.to.FilterClass',
|
||||||
|
'propertyName'=>'propertyValue',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
// return external action classes, e.g.:
|
||||||
|
return array(
|
||||||
|
'action1'=>'path.to.ActionClass',
|
||||||
|
'action2'=>array(
|
||||||
|
'class'=>'path.to.AnotherActionClass',
|
||||||
|
'propertyName'=>'propertyValue',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
26
framework/cli/views/shell/controller/view.php
Normal file
26
framework/cli/views/shell/controller/view.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$controller=substr($controller,0,strlen($controller)-10);
|
||||||
|
$label=ucwords(trim(strtolower(str_replace(array('-','_','.'),' ',preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $controller)))));
|
||||||
|
|
||||||
|
if($action==='index')
|
||||||
|
{
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label',
|
||||||
|
);";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$route=$controller.'/index';
|
||||||
|
$route[0]=strtolower($route[0]);
|
||||||
|
$action=ucfirst($action);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label'=>array('$route'),
|
||||||
|
'$action',
|
||||||
|
);";
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
?>
|
||||||
|
<h1><?php echo '<?php'; ?> echo $this->id . '/' . $this->action->id; ?></h1>
|
||||||
|
|
||||||
|
<p>You may change the content of this page by modifying the file <tt><?php echo '<?php'; ?> echo __FILE__; ?></tt>.</p>
|
42
framework/cli/views/shell/crud/_form.php
Normal file
42
framework/cli/views/shell/crud/_form.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the form view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<div class="form">
|
||||||
|
|
||||||
|
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
|
||||||
|
'id'=>'".$this->class2id($modelClass)."-form',
|
||||||
|
'enableAjaxValidation'=>false,
|
||||||
|
)); ?>\n"; ?>
|
||||||
|
|
||||||
|
<p class="note">Fields with <span class="required">*</span> are required.</p>
|
||||||
|
|
||||||
|
<?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if($column->isPrimaryKey)
|
||||||
|
continue;
|
||||||
|
?>
|
||||||
|
<div class="row">
|
||||||
|
<?php echo "<?php echo ".$this->generateActiveLabel($modelClass,$column)."; ?>\n"; ?>
|
||||||
|
<?php echo "<?php echo ".$this->generateActiveField($modelClass,$column)."; ?>\n"; ?>
|
||||||
|
<?php echo "<?php echo \$form->error(\$model,'{$column->name}'); ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="row buttons">
|
||||||
|
<?php echo "<?php echo CHtml::submitButton(\$model->isNewRecord ? 'Create' : 'Save'); ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
|
||||||
|
|
||||||
|
</div><!-- form -->
|
35
framework/cli/views/shell/crud/_search.php
Normal file
35
framework/cli/views/shell/crud/_search.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the form view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<div class="wide form">
|
||||||
|
|
||||||
|
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
|
||||||
|
'action'=>Yii::app()->createUrl(\$this->route),
|
||||||
|
'method'=>'get',
|
||||||
|
)); ?>\n"; ?>
|
||||||
|
|
||||||
|
<?php foreach($columns as $column): ?>
|
||||||
|
<?php
|
||||||
|
$field=$this->generateInputField($modelClass,$column);
|
||||||
|
if(strpos($field,'password')!==false)
|
||||||
|
continue;
|
||||||
|
?>
|
||||||
|
<div class="row">
|
||||||
|
<?php echo "<?php echo \$form->label(\$model,'{$column->name}'); ?>\n"; ?>
|
||||||
|
<?php echo "<?php echo ".$this->generateActiveField($modelClass,$column)."; ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<div class="row buttons">
|
||||||
|
<?php echo "<?php echo CHtml::submitButton('Search'); ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
|
||||||
|
|
||||||
|
</div><!-- search-form -->
|
29
framework/cli/views/shell/crud/_view.php
Normal file
29
framework/cli/views/shell/crud/_view.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the partial view for rendering a single model.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<div class="view">
|
||||||
|
|
||||||
|
<?php
|
||||||
|
echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$ID}')); ?>:</b>\n";
|
||||||
|
echo "\t<?php echo CHtml::link(CHtml::encode(\$data->{$ID}), array('view', 'id'=>\$data->{$ID})); ?>\n\t<br />\n\n";
|
||||||
|
$count=0;
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if($column->isPrimaryKey)
|
||||||
|
continue;
|
||||||
|
if(++$count==7)
|
||||||
|
echo "\t<?php /*\n";
|
||||||
|
echo "\t<b><?php echo CHtml::encode(\$data->getAttributeLabel('{$column->name}')); ?>:</b>\n";
|
||||||
|
echo "\t<?php echo CHtml::encode(\$data->{$column->name}); ?>\n\t<br />\n\n";
|
||||||
|
}
|
||||||
|
if($count>=7)
|
||||||
|
echo "\t*/ ?>\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
</div>
|
73
framework/cli/views/shell/crud/admin.php
Normal file
73
framework/cli/views/shell/crud/admin.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the admin view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$label=$this->class2name($modelClass,true);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label'=>array('index'),
|
||||||
|
'Manage',
|
||||||
|
);\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
$this->menu=array(
|
||||||
|
array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')),
|
||||||
|
array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')),
|
||||||
|
);
|
||||||
|
|
||||||
|
Yii::app()->clientScript->registerScript('search', "
|
||||||
|
$('.search-button').click(function(){
|
||||||
|
$('.search-form').toggle();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$('.search-form form').submit(function(){
|
||||||
|
$('#<?php echo $this->class2id($modelClass); ?>-grid').yiiGridView('update', {
|
||||||
|
data: $(this).serialize()
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
");
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>Manage <?php echo $this->class2name($modelClass,true); ?></h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may optionally enter a comparison operator (<b><</b>, <b><=</b>, <b>></b>, <b>>=</b>, <b><></b>
|
||||||
|
or <b>=</b>) at the beginning of each of your search values to specify how the comparison should be done.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php echo "<?php echo CHtml::link('Advanced Search','#',array('class'=>'search-button')); ?>"; ?>
|
||||||
|
|
||||||
|
<div class="search-form" style="display:none">
|
||||||
|
<?php echo "<?php \$this->renderPartial('_search',array(
|
||||||
|
'model'=>\$model,
|
||||||
|
)); ?>\n"; ?>
|
||||||
|
</div><!-- search-form -->
|
||||||
|
|
||||||
|
<?php echo "<?php"; ?> $this->widget('zii.widgets.grid.CGridView', array(
|
||||||
|
'id'=>'<?php echo $this->class2id($modelClass); ?>-grid',
|
||||||
|
'dataProvider'=>$model->search(),
|
||||||
|
'filter'=>$model,
|
||||||
|
'columns'=>array(
|
||||||
|
<?php
|
||||||
|
$count=0;
|
||||||
|
foreach($columns as $column)
|
||||||
|
{
|
||||||
|
if(++$count==7)
|
||||||
|
echo "\t\t/*\n";
|
||||||
|
echo "\t\t'".$column->name."',\n";
|
||||||
|
}
|
||||||
|
if($count>=7)
|
||||||
|
echo "\t\t*/\n";
|
||||||
|
?>
|
||||||
|
array(
|
||||||
|
'class'=>'CButtonColumn',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)); ?>
|
190
framework/cli/views/shell/crud/controller.php
Normal file
190
framework/cli/views/shell/crud/controller.php
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the controller class file for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $controllerClass: the controller class name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
class <?php echo $controllerClass; ?> extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the default layout for the views. Defaults to '//layouts/column2', meaning
|
||||||
|
* using two-column layout. See 'protected/views/layouts/column2.php'.
|
||||||
|
*/
|
||||||
|
public $layout='//layouts/column2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CActiveRecord the currently loaded data model instance.
|
||||||
|
*/
|
||||||
|
private $_model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array action filters
|
||||||
|
*/
|
||||||
|
public function filters()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'accessControl', // perform access control for CRUD operations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the access control rules.
|
||||||
|
* This method is used by the 'accessControl' filter.
|
||||||
|
* @return array access control rules
|
||||||
|
*/
|
||||||
|
public function accessRules()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('allow', // allow all users to perform 'index' and 'view' actions
|
||||||
|
'actions'=>array('index','view'),
|
||||||
|
'users'=>array('*'),
|
||||||
|
),
|
||||||
|
array('allow', // allow authenticated user to perform 'create' and 'update' actions
|
||||||
|
'actions'=>array('create','update'),
|
||||||
|
'users'=>array('@'),
|
||||||
|
),
|
||||||
|
array('allow', // allow admin user to perform 'admin' and 'delete' actions
|
||||||
|
'actions'=>array('admin','delete'),
|
||||||
|
'users'=>array('admin'),
|
||||||
|
),
|
||||||
|
array('deny', // deny all users
|
||||||
|
'users'=>array('*'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a particular model.
|
||||||
|
*/
|
||||||
|
public function actionView()
|
||||||
|
{
|
||||||
|
$this->render('view',array(
|
||||||
|
'model'=>$this->loadModel(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new model.
|
||||||
|
* If creation is successful, the browser will be redirected to the 'view' page.
|
||||||
|
*/
|
||||||
|
public function actionCreate()
|
||||||
|
{
|
||||||
|
$model=new <?php echo $modelClass; ?>;
|
||||||
|
|
||||||
|
// Uncomment the following line if AJAX validation is needed
|
||||||
|
// $this->performAjaxValidation($model);
|
||||||
|
|
||||||
|
if(isset($_POST['<?php echo $modelClass; ?>']))
|
||||||
|
{
|
||||||
|
$model->attributes=$_POST['<?php echo $modelClass; ?>'];
|
||||||
|
if($model->save())
|
||||||
|
$this->redirect(array('view','id'=>$model-><?php echo $ID; ?>));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->render('create',array(
|
||||||
|
'model'=>$model,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a particular model.
|
||||||
|
* If update is successful, the browser will be redirected to the 'view' page.
|
||||||
|
*/
|
||||||
|
public function actionUpdate()
|
||||||
|
{
|
||||||
|
$model=$this->loadModel();
|
||||||
|
|
||||||
|
// Uncomment the following line if AJAX validation is needed
|
||||||
|
// $this->performAjaxValidation($model);
|
||||||
|
|
||||||
|
if(isset($_POST['<?php echo $modelClass; ?>']))
|
||||||
|
{
|
||||||
|
$model->attributes=$_POST['<?php echo $modelClass; ?>'];
|
||||||
|
if($model->save())
|
||||||
|
$this->redirect(array('view','id'=>$model-><?php echo $ID; ?>));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->render('update',array(
|
||||||
|
'model'=>$model,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a particular model.
|
||||||
|
* If deletion is successful, the browser will be redirected to the 'index' page.
|
||||||
|
*/
|
||||||
|
public function actionDelete()
|
||||||
|
{
|
||||||
|
if(Yii::app()->request->isPostRequest)
|
||||||
|
{
|
||||||
|
// we only allow deletion via POST request
|
||||||
|
$this->loadModel()->delete();
|
||||||
|
|
||||||
|
// if AJAX request (triggered by deletion via admin grid view), we should not redirect the browser
|
||||||
|
if(!isset($_GET['ajax']))
|
||||||
|
$this->redirect(array('index'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all models.
|
||||||
|
*/
|
||||||
|
public function actionIndex()
|
||||||
|
{
|
||||||
|
$dataProvider=new CActiveDataProvider('<?php echo $modelClass; ?>');
|
||||||
|
$this->render('index',array(
|
||||||
|
'dataProvider'=>$dataProvider,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all models.
|
||||||
|
*/
|
||||||
|
public function actionAdmin()
|
||||||
|
{
|
||||||
|
$model=new <?php echo $modelClass; ?>('search');
|
||||||
|
$model->unsetAttributes(); // clear any default values
|
||||||
|
if(isset($_GET['<?php echo $modelClass; ?>']))
|
||||||
|
$model->attributes=$_GET['<?php echo $modelClass; ?>'];
|
||||||
|
|
||||||
|
$this->render('admin',array(
|
||||||
|
'model'=>$model,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data model based on the primary key given in the GET variable.
|
||||||
|
* If the data model is not found, an HTTP exception will be raised.
|
||||||
|
*/
|
||||||
|
public function loadModel()
|
||||||
|
{
|
||||||
|
if($this->_model===null)
|
||||||
|
{
|
||||||
|
if(isset($_GET['id']))
|
||||||
|
$this->_model=<?php echo $modelClass; ?>::model()->findbyPk($_GET['id']);
|
||||||
|
if($this->_model===null)
|
||||||
|
throw new CHttpException(404,'The requested page does not exist.');
|
||||||
|
}
|
||||||
|
return $this->_model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the AJAX validation.
|
||||||
|
* @param CModel the model to be validated
|
||||||
|
*/
|
||||||
|
protected function performAjaxValidation($model)
|
||||||
|
{
|
||||||
|
if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($modelClass); ?>-form')
|
||||||
|
{
|
||||||
|
echo CActiveForm::validate($model);
|
||||||
|
Yii::app()->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
framework/cli/views/shell/crud/create.php
Normal file
27
framework/cli/views/shell/crud/create.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the create view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$label=$this->class2name($modelClass,true);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label'=>array('index'),
|
||||||
|
'Create',
|
||||||
|
);\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
$this->menu=array(
|
||||||
|
array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')),
|
||||||
|
array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')),
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>Create <?php echo $modelClass; ?></h1>
|
||||||
|
|
||||||
|
<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
|
31
framework/cli/views/shell/crud/index.php
Normal file
31
framework/cli/views/shell/crud/index.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the index view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$label=$this->class2name($modelClass,true);
|
||||||
|
$route=$modelClass.'/index';
|
||||||
|
$route[0]=strtolower($route[0]);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label',
|
||||||
|
);\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
$this->menu=array(
|
||||||
|
array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')),
|
||||||
|
array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')),
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1><?php echo $label; ?></h1>
|
||||||
|
|
||||||
|
<?php echo "<?php"; ?> $this->widget('zii.widgets.CListView', array(
|
||||||
|
'dataProvider'=>$dataProvider,
|
||||||
|
'itemView'=>'_view',
|
||||||
|
)); ?>
|
47
framework/cli/views/shell/crud/test.php
Normal file
47
framework/cli/views/shell/crud/test.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the functional test for controller.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $controllerID: the controller ID
|
||||||
|
* - $fixtureName: the fixture name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
class <?php echo $modelClass; ?>Test extends WebTestCase
|
||||||
|
{
|
||||||
|
public $fixtures=array(
|
||||||
|
'<?php echo $fixtureName; ?>'=>'<?php echo $modelClass; ?>',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function testShow()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/view&id=1');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreate()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUpdate()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/update&id=1');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDelete()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/view&id=1');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testList()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAdmin()
|
||||||
|
{
|
||||||
|
$this->open('?r=<?php echo $controllerID; ?>/admin');
|
||||||
|
}
|
||||||
|
}
|
31
framework/cli/views/shell/crud/update.php
Normal file
31
framework/cli/views/shell/crud/update.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the update view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$nameColumn=$this->guessNameColumn($columns);
|
||||||
|
$label=$this->class2name($modelClass,true);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label'=>array('index'),
|
||||||
|
\$model->{$nameColumn}=>array('view','id'=>\$model->{$ID}),
|
||||||
|
'Update',
|
||||||
|
);\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
$this->menu=array(
|
||||||
|
array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')),
|
||||||
|
array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')),
|
||||||
|
array('label'=>'View <?php echo $modelClass; ?>', 'url'=>array('view', 'id'=>$model-><?php echo $ID; ?>)),
|
||||||
|
array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')),
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>Update <?php echo $modelClass." <?php echo \$model->{$ID}; ?>"; ?></h1>
|
||||||
|
|
||||||
|
<?php echo "<?php echo \$this->renderPartial('_form', array('model'=>\$model)); ?>"; ?>
|
39
framework/cli/views/shell/crud/view.php
Normal file
39
framework/cli/views/shell/crud/view.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the 'view' view for crud.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $ID: the primary key name
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $columns: a list of column schema objects
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
echo "<?php\n";
|
||||||
|
$nameColumn=$this->guessNameColumn($columns);
|
||||||
|
$label=$this->class2name($modelClass,true);
|
||||||
|
echo "\$this->breadcrumbs=array(
|
||||||
|
'$label'=>array('index'),
|
||||||
|
\$model->{$nameColumn},
|
||||||
|
);\n";
|
||||||
|
?>
|
||||||
|
|
||||||
|
$this->menu=array(
|
||||||
|
array('label'=>'List <?php echo $modelClass; ?>', 'url'=>array('index')),
|
||||||
|
array('label'=>'Create <?php echo $modelClass; ?>', 'url'=>array('create')),
|
||||||
|
array('label'=>'Update <?php echo $modelClass; ?>', 'url'=>array('update', 'id'=>$model-><?php echo $ID; ?>)),
|
||||||
|
array('label'=>'Delete <?php echo $modelClass; ?>', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model-><?php echo $ID; ?>),'confirm'=>'Are you sure you want to delete this item?')),
|
||||||
|
array('label'=>'Manage <?php echo $modelClass; ?>', 'url'=>array('admin')),
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h1>View <?php echo $modelClass." #<?php echo \$model->{$ID}; ?>"; ?></h1>
|
||||||
|
|
||||||
|
<?php echo "<?php"; ?> $this->widget('zii.widgets.CDetailView', array(
|
||||||
|
'data'=>$model,
|
||||||
|
'attributes'=>array(
|
||||||
|
<?php
|
||||||
|
foreach($columns as $column)
|
||||||
|
echo "\t\t'".$column->name."',\n";
|
||||||
|
?>
|
||||||
|
),
|
||||||
|
)); ?>
|
37
framework/cli/views/shell/form/action.php
Normal file
37
framework/cli/views/shell/form/action.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the action script for the form.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $viewName: the name of the view
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php
|
||||||
|
$actionName=$modelClass;
|
||||||
|
if(strrpos($modelClass,'Form')===strlen($modelClass)-4)
|
||||||
|
$actionName=substr($modelClass,0,strlen($modelClass)-4);
|
||||||
|
?>
|
||||||
|
public function action<?php echo $actionName; ?>()
|
||||||
|
{
|
||||||
|
$model=new <?php echo $modelClass; ?>;
|
||||||
|
|
||||||
|
// uncomment the following code to enable ajax-based validation
|
||||||
|
/*
|
||||||
|
if(isset($_POST['ajax']) && $_POST['ajax']==='<?php echo $this->class2id($modelClass); ?>-form')
|
||||||
|
{
|
||||||
|
echo CActiveForm::validate($model);
|
||||||
|
Yii::app()->end();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(isset($_POST['<?php echo $modelClass; ?>']))
|
||||||
|
{
|
||||||
|
$model->attributes=$_POST['<?php echo $modelClass; ?>'];
|
||||||
|
if($model->validate())
|
||||||
|
{
|
||||||
|
// form inputs are valid, do something here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->render('<?php echo $viewName; ?>',array('model'=>$model));
|
||||||
|
}
|
39
framework/cli/views/shell/form/form.php
Normal file
39
framework/cli/views/shell/form/form.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the form view for the specified model.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $modelClass: the model class name
|
||||||
|
* - $attributes: a list of attribute names to receive form inputs
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<div class="form">
|
||||||
|
|
||||||
|
<?php echo "<?php \$form=\$this->beginWidget('CActiveForm', array(
|
||||||
|
'id'=>'".$this->class2id($modelClass)."-form',
|
||||||
|
'enableAjaxValidation'=>false,
|
||||||
|
)); ?>\n"; ?>
|
||||||
|
|
||||||
|
<p class="note">Fields with <span class="required">*</span> are required.</p>
|
||||||
|
|
||||||
|
<?php echo "<?php echo \$form->errorSummary(\$model); ?>\n"; ?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
foreach($attributes as $attribute)
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<div class="row">
|
||||||
|
<?php echo "<?php echo \$form->labelEx(\$model,'$attribute'); ?>\n"; ?>
|
||||||
|
<?php echo "<?php echo \$form->textField(\$model,'$attribute'); ?>\n"; ?>
|
||||||
|
<?php echo "<?php echo \$form->error(\$model,'$attribute'); ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="row buttons">
|
||||||
|
<?php echo "<?php echo CHtml::submitButton('Submit'); ?>\n"; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php echo "<?php \$this->endWidget(); ?>\n"; ?>
|
||||||
|
|
||||||
|
</div><!-- form -->
|
25
framework/cli/views/shell/model/fixture.php
Normal file
25
framework/cli/views/shell/model/fixture.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the fixture file for a model class.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $table: the table schema
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
return array(
|
||||||
|
/*
|
||||||
|
'sample1'=>array(
|
||||||
|
<?php foreach($table->columns as $name=>$column) {
|
||||||
|
if($table->sequenceName===null || $table->primaryKey!==$column->name)
|
||||||
|
echo "\t\t'$name' => '',\n";
|
||||||
|
} ?>
|
||||||
|
),
|
||||||
|
'sample2'=>array(
|
||||||
|
<?php foreach($table->columns as $name=>$column) {
|
||||||
|
if($table->sequenceName===null || $table->primaryKey!==$column->name)
|
||||||
|
echo "\t\t'$name' => '',\n";
|
||||||
|
} ?>
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
);
|
120
framework/cli/views/shell/model/model.php
Normal file
120
framework/cli/views/shell/model/model.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating a model class file.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $className: the class name
|
||||||
|
* - $tableName: the table name
|
||||||
|
* - $columns: a list of table column schema objects
|
||||||
|
* - $rules: a list of validation rules (string)
|
||||||
|
* - $labels: a list of labels (column name => label)
|
||||||
|
* - $relations: a list of relations (string)
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the model class for table "<?php echo $tableName; ?>".
|
||||||
|
*
|
||||||
|
* The followings are the available columns in table '<?php echo $tableName; ?>':
|
||||||
|
<?php foreach($columns as $column): ?>
|
||||||
|
* @property <?php echo $column->type.' $'.$column->name."\n"; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
*/
|
||||||
|
class <?php echo $className; ?> extends CActiveRecord
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string the associated database table name
|
||||||
|
*/
|
||||||
|
public function tableName()
|
||||||
|
{
|
||||||
|
return '<?php echo $tableName; ?>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array validation rules for model attributes.
|
||||||
|
*/
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
// NOTE: you should only define rules for those attributes that
|
||||||
|
// will receive user inputs.
|
||||||
|
return array(
|
||||||
|
<?php foreach($rules as $rule): ?>
|
||||||
|
<?php echo $rule.",\n"; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
// The following rule is used by search().
|
||||||
|
// Please remove those attributes that should not be searched.
|
||||||
|
array('<?php echo implode(', ', array_keys($columns)); ?>', 'safe', 'on'=>'search'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array relational rules.
|
||||||
|
*/
|
||||||
|
public function relations()
|
||||||
|
{
|
||||||
|
// NOTE: you may need to adjust the relation name and the related
|
||||||
|
// class name for the relations automatically generated below.
|
||||||
|
return array(
|
||||||
|
<?php foreach($relations as $name=>$relation): ?>
|
||||||
|
<?php echo "'$name' => $relation,\n"; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array customized attribute labels (name=>label)
|
||||||
|
*/
|
||||||
|
public function attributeLabels()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
<?php foreach($labels as $column=>$label): ?>
|
||||||
|
<?php echo "'$column' => '$label',\n"; ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of models based on the current search/filter conditions.
|
||||||
|
*
|
||||||
|
* Typical usecase:
|
||||||
|
* - Initialize the model fields with values from filter form.
|
||||||
|
* - Execute this method to get CActiveDataProvider instance which will filter
|
||||||
|
* models according to data in model fields.
|
||||||
|
* - Pass data provider to CGridView, CListView or any similar widget.
|
||||||
|
*
|
||||||
|
* @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
|
||||||
|
*/
|
||||||
|
public function search()
|
||||||
|
{
|
||||||
|
// Warning: Please modify the following code to remove attributes that
|
||||||
|
// should not be searched.
|
||||||
|
|
||||||
|
$criteria=new CDbCriteria;
|
||||||
|
|
||||||
|
<?php
|
||||||
|
foreach($columns as $name=>$column)
|
||||||
|
{
|
||||||
|
if($column->type==='string')
|
||||||
|
{
|
||||||
|
echo "\t\t\$criteria->compare('$name',\$this->$name,true);\n\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo "\t\t\$criteria->compare('$name',\$this->$name);\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
return new CActiveDataProvider('<?php echo $className; ?>', array(
|
||||||
|
'criteria'=>$criteria,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the static model of the specified AR class.
|
||||||
|
* @return <?php echo $className; ?> the static model class
|
||||||
|
*/
|
||||||
|
public static function model($className=__CLASS__)
|
||||||
|
{
|
||||||
|
return parent::model($className);
|
||||||
|
}
|
||||||
|
}
|
21
framework/cli/views/shell/model/test.php
Normal file
21
framework/cli/views/shell/model/test.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the template for generating the unit test for a model class.
|
||||||
|
* The following variables are available in this template:
|
||||||
|
* - $className: the class name
|
||||||
|
* - $fixtureName: the fixture name
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
class <?php echo $className; ?>Test extends CDbTestCase
|
||||||
|
{
|
||||||
|
public $fixtures=array(
|
||||||
|
'<?php echo $fixtureName; ?>'=>'<?php echo $className; ?>',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function testCreate()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class DefaultController extends Controller
|
||||||
|
{
|
||||||
|
public function actionIndex()
|
||||||
|
{
|
||||||
|
$this->render('index');
|
||||||
|
}
|
||||||
|
}
|
28
framework/cli/views/shell/module/module.php
Normal file
28
framework/cli/views/shell/module/module.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php echo "<?php\n"; ?>
|
||||||
|
|
||||||
|
class <?php echo $moduleClass; ?> extends CWebModule
|
||||||
|
{
|
||||||
|
public function init()
|
||||||
|
{
|
||||||
|
// this method is called when the module is being created
|
||||||
|
// you may place code here to customize the module or the application
|
||||||
|
|
||||||
|
// import the module-level models and components
|
||||||
|
$this->setImport(array(
|
||||||
|
'<?php echo $moduleID; ?>.models.*',
|
||||||
|
'<?php echo $moduleID; ?>.components.*',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeControllerAction($controller, $action)
|
||||||
|
{
|
||||||
|
if(parent::beforeControllerAction($controller, $action))
|
||||||
|
{
|
||||||
|
// this method is called before any module controller action is performed
|
||||||
|
// you may place customized code here
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
14
framework/cli/views/shell/module/views/default/index.php
Normal file
14
framework/cli/views/shell/module/views/default/index.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
$this->breadcrumbs=array(
|
||||||
|
$this->module->id,
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
<h1><?php echo $this->uniqueId . '/' . $this->action->id; ?></h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This is the view content for action "<?php echo $this->action->id; ?>".
|
||||||
|
The action belongs to the controller "<?php echo get_class($this); ?>" in the "<?php echo $this->module->id; ?>" module.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You may customize this page by editing <tt><?php echo __FILE__; ?></tt>
|
||||||
|
</p>
|
2
framework/cli/views/webapp/assets/git-gitignore
Normal file
2
framework/cli/views/webapp/assets/git-gitignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
0
framework/cli/views/webapp/assets/hg-hgkeep
Normal file
0
framework/cli/views/webapp/assets/hg-hgkeep
Normal file
BIN
framework/cli/views/webapp/css/bg.gif
Normal file
BIN
framework/cli/views/webapp/css/bg.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 243 B |
164
framework/cli/views/webapp/css/form.css
Normal file
164
framework/cli/views/webapp/css/form.css
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/**
|
||||||
|
* CSS styles for forms generated by yiic.
|
||||||
|
*
|
||||||
|
* The styles can be applied to the following form structure:
|
||||||
|
*
|
||||||
|
* <div class="form">
|
||||||
|
* <div class="row">
|
||||||
|
* <label for="inputid">xyz</label>
|
||||||
|
* <input name="inputid" id="inputid" type="text" />
|
||||||
|
* <p class="hint">hint text</p>
|
||||||
|
* </div>
|
||||||
|
* <div class="row">
|
||||||
|
* <label for="inputid">xyz</label>
|
||||||
|
* <input name="inputid" id="inputid" type="text" />
|
||||||
|
* <p class="hint">hint text</p>
|
||||||
|
* </div>
|
||||||
|
* <div class="row buttons">
|
||||||
|
* <label for="inputid">xyz</label>
|
||||||
|
* <input name="inputid" id="inputid" type="text" />
|
||||||
|
* <p class="hint">hint text</p>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
* The above code will render the labels and input fields in separate lines.
|
||||||
|
* In order to render them in the same line, please use the "wide" form as follows,
|
||||||
|
*
|
||||||
|
* <div class="wide form">
|
||||||
|
* ......
|
||||||
|
* </div>
|
||||||
|
*
|
||||||
|
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||||
|
* @link http://www.yiiframework.com/
|
||||||
|
* @copyright 2008-2010 Yii Software LLC
|
||||||
|
* @license http://www.yiiframework.com/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
div.form
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form input,
|
||||||
|
div.form textarea,
|
||||||
|
div.form select
|
||||||
|
{
|
||||||
|
margin: 0.2em 0 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form fieldset
|
||||||
|
{
|
||||||
|
border: 1px solid #DDD;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
-moz-border-radius:7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form label
|
||||||
|
{
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .row
|
||||||
|
{
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .hint
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .note
|
||||||
|
{
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form span.required
|
||||||
|
{
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form div.error label:first-child,
|
||||||
|
div.form label.error,
|
||||||
|
div.form span.error
|
||||||
|
{
|
||||||
|
color: #C00;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form div.error input,
|
||||||
|
div.form div.error textarea,
|
||||||
|
div.form div.error select,
|
||||||
|
div.form input.error,
|
||||||
|
div.form textarea.error,
|
||||||
|
div.form select.error
|
||||||
|
{
|
||||||
|
background: #FEE;
|
||||||
|
border-color: #C00;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form div.success input,
|
||||||
|
div.form div.success textarea,
|
||||||
|
div.form div.success select,
|
||||||
|
div.form input.success,
|
||||||
|
div.form textarea.success,
|
||||||
|
div.form select.success
|
||||||
|
{
|
||||||
|
background: #E6EFC2;
|
||||||
|
border-color: #C6D880;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form div.success label
|
||||||
|
{
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .errorSummary
|
||||||
|
{
|
||||||
|
border: 2px solid #C00;
|
||||||
|
padding: 7px 7px 12px 7px;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
background: #FEE;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .errorMessage
|
||||||
|
{
|
||||||
|
color: red;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .errorSummary p
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .errorSummary ul
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.wide.form label
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
margin-right: 10px;
|
||||||
|
position: relative;
|
||||||
|
text-align: right;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.wide.form .row
|
||||||
|
{
|
||||||
|
clear: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.wide.form .buttons, div.wide.form .hint, div.wide.form .errorMessage
|
||||||
|
{
|
||||||
|
clear: left;
|
||||||
|
padding-left: 110px;
|
||||||
|
}
|
36
framework/cli/views/webapp/css/ie.css
Normal file
36
framework/cli/views/webapp/css/ie.css
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint CSS Framework 1.0.1
|
||||||
|
http://blueprintcss.org
|
||||||
|
|
||||||
|
* Copyright (c) 2007-Present. See LICENSE for more info.
|
||||||
|
* See README for instructions on how to use Blueprint.
|
||||||
|
* For credits and origins, see AUTHORS.
|
||||||
|
* This is a compressed file. See the sources in the 'src' directory.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* ie.css */
|
||||||
|
body {text-align:center;}
|
||||||
|
.container {text-align:left;}
|
||||||
|
* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 {display:inline;overflow-x:hidden;}
|
||||||
|
* html legend {margin:0px -8px 16px 0;padding:0;}
|
||||||
|
sup {vertical-align:text-top;}
|
||||||
|
sub {vertical-align:text-bottom;}
|
||||||
|
html>body p code {*white-space:normal;}
|
||||||
|
hr {margin:-8px auto 11px;}
|
||||||
|
img {-ms-interpolation-mode:bicubic;}
|
||||||
|
.clearfix, .container {display:inline-block;}
|
||||||
|
* html .clearfix, * html .container {height:1%;}
|
||||||
|
fieldset {padding-top:0;}
|
||||||
|
legend {margin-top:-0.2em;margin-bottom:1em;margin-left:-0.5em;}
|
||||||
|
textarea {overflow:auto;}
|
||||||
|
label {vertical-align:middle;position:relative;top:-0.25em;}
|
||||||
|
input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;}
|
||||||
|
input.text:focus, input.title:focus {border-color:#666;}
|
||||||
|
input.text, input.title, textarea, select {margin:0.5em 0;}
|
||||||
|
input.checkbox, input.radio {position:relative;top:.25em;}
|
||||||
|
form.inline div, form.inline p {vertical-align:middle;}
|
||||||
|
form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;}
|
||||||
|
button, input.button {position:relative;top:0.25em;}
|
229
framework/cli/views/webapp/css/main.css
Normal file
229
framework/cli/views/webapp/css/main.css
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
body
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #555;
|
||||||
|
font: normal 10pt Arial,Helvetica,sans-serif;
|
||||||
|
background: #EFEFEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#page
|
||||||
|
{
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #C9E0ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header
|
||||||
|
{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-top: 3px solid #C9E0ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content
|
||||||
|
{
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar
|
||||||
|
{
|
||||||
|
padding: 20px 20px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer
|
||||||
|
{
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 20px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #C9E0ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo
|
||||||
|
{
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 200%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainmenu
|
||||||
|
{
|
||||||
|
background:white url(bg.gif) repeat-x left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainmenu ul
|
||||||
|
{
|
||||||
|
padding:6px 20px 5px 20px;
|
||||||
|
margin:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainmenu ul li
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainmenu ul li a
|
||||||
|
{
|
||||||
|
color:#ffffff;
|
||||||
|
background-color:transparent;
|
||||||
|
font-size:12px;
|
||||||
|
font-weight:bold;
|
||||||
|
text-decoration:none;
|
||||||
|
padding:5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mainmenu ul li a:hover, #mainmenu ul li.active a
|
||||||
|
{
|
||||||
|
color: #6399cd;
|
||||||
|
background-color:#EFF4FA;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-error, div.flash-notice, div.flash-success
|
||||||
|
{
|
||||||
|
padding:.8em;
|
||||||
|
margin-bottom:1em;
|
||||||
|
border:2px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-error
|
||||||
|
{
|
||||||
|
background:#FBE3E4;
|
||||||
|
color:#8a1f11;
|
||||||
|
border-color:#FBC2C4;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-notice
|
||||||
|
{
|
||||||
|
background:#FFF6BF;
|
||||||
|
color:#514721;
|
||||||
|
border-color:#FFD324;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-success
|
||||||
|
{
|
||||||
|
background:#E6EFC2;
|
||||||
|
color:#264409;
|
||||||
|
border-color:#C6D880;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-error a
|
||||||
|
{
|
||||||
|
color:#8a1f11;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-notice a
|
||||||
|
{
|
||||||
|
color:#514721;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.flash-success a
|
||||||
|
{
|
||||||
|
color:#264409;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.form .rememberMe label
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.view
|
||||||
|
{
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
border: 1px solid #C9E0ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.breadcrumbs
|
||||||
|
{
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 5px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.breadcrumbs span
|
||||||
|
{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.search-form
|
||||||
|
{
|
||||||
|
padding: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet-decoration
|
||||||
|
{
|
||||||
|
padding: 3px 8px;
|
||||||
|
background: #B7D6E7;
|
||||||
|
border-left: 5px solid #6FACCF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet-title
|
||||||
|
{
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
color: #298dcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet-content
|
||||||
|
{
|
||||||
|
font-size:0.9em;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
padding: 5px 8px;
|
||||||
|
background:#EFFDFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet-content ul
|
||||||
|
{
|
||||||
|
list-style-image:none;
|
||||||
|
list-style-position:outside;
|
||||||
|
list-style-type:none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portlet-content li
|
||||||
|
{
|
||||||
|
padding: 2px 0 4px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations
|
||||||
|
{
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations li
|
||||||
|
{
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations li a
|
||||||
|
{
|
||||||
|
font: bold 12px Arial;
|
||||||
|
color: #0066A4;
|
||||||
|
display: block;
|
||||||
|
padding: 2px 0 2px 8px;
|
||||||
|
line-height: 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations li a:visited
|
||||||
|
{
|
||||||
|
color: #0066A4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations li a:hover
|
||||||
|
{
|
||||||
|
background: #80CFFF;
|
||||||
|
}
|
29
framework/cli/views/webapp/css/print.css
Normal file
29
framework/cli/views/webapp/css/print.css
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint CSS Framework 1.0.1
|
||||||
|
http://blueprintcss.org
|
||||||
|
|
||||||
|
* Copyright (c) 2007-Present. See LICENSE for more info.
|
||||||
|
* See README for instructions on how to use Blueprint.
|
||||||
|
* For credits and origins, see AUTHORS.
|
||||||
|
* This is a compressed file. See the sources in the 'src' directory.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* print.css */
|
||||||
|
body {line-height:1.5;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;color:#000;background:none;font-size:10pt;}
|
||||||
|
.container {background:none;}
|
||||||
|
hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;}
|
||||||
|
hr.space {background:#fff;color:#fff;visibility:hidden;}
|
||||||
|
h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;}
|
||||||
|
code {font:.9em "Courier New", Monaco, Courier, monospace;}
|
||||||
|
a img {border:none;}
|
||||||
|
p img.top {margin-top:0;}
|
||||||
|
blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;}
|
||||||
|
.small {font-size:.9em;}
|
||||||
|
.large {font-size:1.1em;}
|
||||||
|
.quiet {color:#999;}
|
||||||
|
.hide {display:none;}
|
||||||
|
a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;}
|
||||||
|
a:link:after, a:visited:after {content:" (" attr(href) ")";font-size:90%;}
|
238
framework/cli/views/webapp/css/screen.css
Normal file
238
framework/cli/views/webapp/css/screen.css
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/* -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint CSS Framework 1.0.1
|
||||||
|
http://blueprintcss.org
|
||||||
|
|
||||||
|
* Copyright (c) 2007-Present. See LICENSE for more info.
|
||||||
|
* See README for instructions on how to use Blueprint.
|
||||||
|
* For credits and origins, see AUTHORS.
|
||||||
|
* This is a compressed file. See the sources in the 'src' directory.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* reset.css */
|
||||||
|
html {margin:0;padding:0;border:0;}
|
||||||
|
body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section {margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;}
|
||||||
|
article, aside, details, figcaption, figure, dialog, footer, header, hgroup, menu, nav, section {display:block;}
|
||||||
|
body {line-height:1.5;background:white;}
|
||||||
|
table {border-collapse:separate;border-spacing:0;}
|
||||||
|
caption, th, td {text-align:left;font-weight:normal;float:none !important;}
|
||||||
|
table, th, td {vertical-align:middle;}
|
||||||
|
blockquote:before, blockquote:after, q:before, q:after {content:'';}
|
||||||
|
blockquote, q {quotes:"" "";}
|
||||||
|
a img {border:none;}
|
||||||
|
:focus {outline:0;}
|
||||||
|
|
||||||
|
/* typography.css */
|
||||||
|
html {font-size:100.01%;}
|
||||||
|
body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;}
|
||||||
|
h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;}
|
||||||
|
h1 {font-size:2em;line-height:1;margin-bottom:0.5em;}
|
||||||
|
h2 {font-size:1.6em;margin-bottom:0.75em;}
|
||||||
|
h3 {font-size:1.4em;line-height:1;margin-bottom:1em;}
|
||||||
|
h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;}
|
||||||
|
h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;}
|
||||||
|
h6 {font-size:1em;font-weight:bold;}
|
||||||
|
h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;}
|
||||||
|
p {margin:0 0 1.5em;}
|
||||||
|
.left {float:left !important;}
|
||||||
|
p .left {margin:1.5em 1.5em 1.5em 0;padding:0;}
|
||||||
|
.right {float:right !important;}
|
||||||
|
p .right {margin:1.5em 0 1.5em 1.5em;padding:0;}
|
||||||
|
a:focus, a:hover {color:#09f;}
|
||||||
|
a {color:#06c;text-decoration:underline;}
|
||||||
|
blockquote {margin:1.5em;color:#666;font-style:italic;}
|
||||||
|
strong, dfn {font-weight:bold;}
|
||||||
|
em, dfn {font-style:italic;}
|
||||||
|
sup, sub {line-height:0;}
|
||||||
|
abbr, acronym {border-bottom:1px dotted #666;}
|
||||||
|
address {margin:0 0 1.5em;font-style:italic;}
|
||||||
|
del {color:#666;}
|
||||||
|
pre {margin:1.5em 0;white-space:pre;}
|
||||||
|
pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;}
|
||||||
|
li ul, li ol {margin:0;}
|
||||||
|
ul, ol {margin:0 1.5em 1.5em 0;padding-left:1.5em;}
|
||||||
|
ul {list-style-type:disc;}
|
||||||
|
ol {list-style-type:decimal;}
|
||||||
|
dl {margin:0 0 1.5em 0;}
|
||||||
|
dl dt {font-weight:bold;}
|
||||||
|
dd {margin-left:1.5em;}
|
||||||
|
table {margin-bottom:1.4em;width:100%;}
|
||||||
|
th {font-weight:bold;}
|
||||||
|
thead th {background:#c3d9ff;}
|
||||||
|
th, td, caption {padding:4px 10px 4px 5px;}
|
||||||
|
tfoot {font-style:italic;}
|
||||||
|
caption {background:#eee;}
|
||||||
|
.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;}
|
||||||
|
.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;}
|
||||||
|
.hide {display:none;}
|
||||||
|
.quiet {color:#666;}
|
||||||
|
.loud {color:#000;}
|
||||||
|
.highlight {background:#ff0;}
|
||||||
|
.added {background:#060;color:#fff;}
|
||||||
|
.removed {background:#900;color:#fff;}
|
||||||
|
.first {margin-left:0;padding-left:0;}
|
||||||
|
.last {margin-right:0;padding-right:0;}
|
||||||
|
.top {margin-top:0;padding-top:0;}
|
||||||
|
.bottom {margin-bottom:0;padding-bottom:0;}
|
||||||
|
|
||||||
|
/* grid.css */
|
||||||
|
.container {width:950px;margin:0 auto;}
|
||||||
|
.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 {float:left;margin-right:10px;}
|
||||||
|
.last {margin-right:0;}
|
||||||
|
.span-1 {width:30px;}
|
||||||
|
.span-2 {width:70px;}
|
||||||
|
.span-3 {width:110px;}
|
||||||
|
.span-4 {width:150px;}
|
||||||
|
.span-5 {width:190px;}
|
||||||
|
.span-6 {width:230px;}
|
||||||
|
.span-7 {width:270px;}
|
||||||
|
.span-8 {width:310px;}
|
||||||
|
.span-9 {width:350px;}
|
||||||
|
.span-10 {width:390px;}
|
||||||
|
.span-11 {width:430px;}
|
||||||
|
.span-12 {width:470px;}
|
||||||
|
.span-13 {width:510px;}
|
||||||
|
.span-14 {width:550px;}
|
||||||
|
.span-15 {width:590px;}
|
||||||
|
.span-16 {width:630px;}
|
||||||
|
.span-17 {width:670px;}
|
||||||
|
.span-18 {width:710px;}
|
||||||
|
.span-19 {width:750px;}
|
||||||
|
.span-20 {width:790px;}
|
||||||
|
.span-21 {width:830px;}
|
||||||
|
.span-22 {width:870px;}
|
||||||
|
.span-23 {width:910px;}
|
||||||
|
.span-24 {width:950px;margin-right:0;}
|
||||||
|
input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px;border-right-width:1px;padding-left:5px;padding-right:5px;}
|
||||||
|
input.span-1, textarea.span-1 {width:18px;}
|
||||||
|
input.span-2, textarea.span-2 {width:58px;}
|
||||||
|
input.span-3, textarea.span-3 {width:98px;}
|
||||||
|
input.span-4, textarea.span-4 {width:138px;}
|
||||||
|
input.span-5, textarea.span-5 {width:178px;}
|
||||||
|
input.span-6, textarea.span-6 {width:218px;}
|
||||||
|
input.span-7, textarea.span-7 {width:258px;}
|
||||||
|
input.span-8, textarea.span-8 {width:298px;}
|
||||||
|
input.span-9, textarea.span-9 {width:338px;}
|
||||||
|
input.span-10, textarea.span-10 {width:378px;}
|
||||||
|
input.span-11, textarea.span-11 {width:418px;}
|
||||||
|
input.span-12, textarea.span-12 {width:458px;}
|
||||||
|
input.span-13, textarea.span-13 {width:498px;}
|
||||||
|
input.span-14, textarea.span-14 {width:538px;}
|
||||||
|
input.span-15, textarea.span-15 {width:578px;}
|
||||||
|
input.span-16, textarea.span-16 {width:618px;}
|
||||||
|
input.span-17, textarea.span-17 {width:658px;}
|
||||||
|
input.span-18, textarea.span-18 {width:698px;}
|
||||||
|
input.span-19, textarea.span-19 {width:738px;}
|
||||||
|
input.span-20, textarea.span-20 {width:778px;}
|
||||||
|
input.span-21, textarea.span-21 {width:818px;}
|
||||||
|
input.span-22, textarea.span-22 {width:858px;}
|
||||||
|
input.span-23, textarea.span-23 {width:898px;}
|
||||||
|
input.span-24, textarea.span-24 {width:938px;}
|
||||||
|
.append-1 {padding-right:40px;}
|
||||||
|
.append-2 {padding-right:80px;}
|
||||||
|
.append-3 {padding-right:120px;}
|
||||||
|
.append-4 {padding-right:160px;}
|
||||||
|
.append-5 {padding-right:200px;}
|
||||||
|
.append-6 {padding-right:240px;}
|
||||||
|
.append-7 {padding-right:280px;}
|
||||||
|
.append-8 {padding-right:320px;}
|
||||||
|
.append-9 {padding-right:360px;}
|
||||||
|
.append-10 {padding-right:400px;}
|
||||||
|
.append-11 {padding-right:440px;}
|
||||||
|
.append-12 {padding-right:480px;}
|
||||||
|
.append-13 {padding-right:520px;}
|
||||||
|
.append-14 {padding-right:560px;}
|
||||||
|
.append-15 {padding-right:600px;}
|
||||||
|
.append-16 {padding-right:640px;}
|
||||||
|
.append-17 {padding-right:680px;}
|
||||||
|
.append-18 {padding-right:720px;}
|
||||||
|
.append-19 {padding-right:760px;}
|
||||||
|
.append-20 {padding-right:800px;}
|
||||||
|
.append-21 {padding-right:840px;}
|
||||||
|
.append-22 {padding-right:880px;}
|
||||||
|
.append-23 {padding-right:920px;}
|
||||||
|
.prepend-1 {padding-left:40px;}
|
||||||
|
.prepend-2 {padding-left:80px;}
|
||||||
|
.prepend-3 {padding-left:120px;}
|
||||||
|
.prepend-4 {padding-left:160px;}
|
||||||
|
.prepend-5 {padding-left:200px;}
|
||||||
|
.prepend-6 {padding-left:240px;}
|
||||||
|
.prepend-7 {padding-left:280px;}
|
||||||
|
.prepend-8 {padding-left:320px;}
|
||||||
|
.prepend-9 {padding-left:360px;}
|
||||||
|
.prepend-10 {padding-left:400px;}
|
||||||
|
.prepend-11 {padding-left:440px;}
|
||||||
|
.prepend-12 {padding-left:480px;}
|
||||||
|
.prepend-13 {padding-left:520px;}
|
||||||
|
.prepend-14 {padding-left:560px;}
|
||||||
|
.prepend-15 {padding-left:600px;}
|
||||||
|
.prepend-16 {padding-left:640px;}
|
||||||
|
.prepend-17 {padding-left:680px;}
|
||||||
|
.prepend-18 {padding-left:720px;}
|
||||||
|
.prepend-19 {padding-left:760px;}
|
||||||
|
.prepend-20 {padding-left:800px;}
|
||||||
|
.prepend-21 {padding-left:840px;}
|
||||||
|
.prepend-22 {padding-left:880px;}
|
||||||
|
.prepend-23 {padding-left:920px;}
|
||||||
|
.border {padding-right:4px;margin-right:5px;border-right:1px solid #ddd;}
|
||||||
|
.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #ddd;}
|
||||||
|
.pull-1 {margin-left:-40px;}
|
||||||
|
.pull-2 {margin-left:-80px;}
|
||||||
|
.pull-3 {margin-left:-120px;}
|
||||||
|
.pull-4 {margin-left:-160px;}
|
||||||
|
.pull-5 {margin-left:-200px;}
|
||||||
|
.pull-6 {margin-left:-240px;}
|
||||||
|
.pull-7 {margin-left:-280px;}
|
||||||
|
.pull-8 {margin-left:-320px;}
|
||||||
|
.pull-9 {margin-left:-360px;}
|
||||||
|
.pull-10 {margin-left:-400px;}
|
||||||
|
.pull-11 {margin-left:-440px;}
|
||||||
|
.pull-12 {margin-left:-480px;}
|
||||||
|
.pull-13 {margin-left:-520px;}
|
||||||
|
.pull-14 {margin-left:-560px;}
|
||||||
|
.pull-15 {margin-left:-600px;}
|
||||||
|
.pull-16 {margin-left:-640px;}
|
||||||
|
.pull-17 {margin-left:-680px;}
|
||||||
|
.pull-18 {margin-left:-720px;}
|
||||||
|
.pull-19 {margin-left:-760px;}
|
||||||
|
.pull-20 {margin-left:-800px;}
|
||||||
|
.pull-21 {margin-left:-840px;}
|
||||||
|
.pull-22 {margin-left:-880px;}
|
||||||
|
.pull-23 {margin-left:-920px;}
|
||||||
|
.pull-24 {margin-left:-960px;}
|
||||||
|
.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;}
|
||||||
|
.push-1 {margin:0 -40px 1.5em 40px;}
|
||||||
|
.push-2 {margin:0 -80px 1.5em 80px;}
|
||||||
|
.push-3 {margin:0 -120px 1.5em 120px;}
|
||||||
|
.push-4 {margin:0 -160px 1.5em 160px;}
|
||||||
|
.push-5 {margin:0 -200px 1.5em 200px;}
|
||||||
|
.push-6 {margin:0 -240px 1.5em 240px;}
|
||||||
|
.push-7 {margin:0 -280px 1.5em 280px;}
|
||||||
|
.push-8 {margin:0 -320px 1.5em 320px;}
|
||||||
|
.push-9 {margin:0 -360px 1.5em 360px;}
|
||||||
|
.push-10 {margin:0 -400px 1.5em 400px;}
|
||||||
|
.push-11 {margin:0 -440px 1.5em 440px;}
|
||||||
|
.push-12 {margin:0 -480px 1.5em 480px;}
|
||||||
|
.push-13 {margin:0 -520px 1.5em 520px;}
|
||||||
|
.push-14 {margin:0 -560px 1.5em 560px;}
|
||||||
|
.push-15 {margin:0 -600px 1.5em 600px;}
|
||||||
|
.push-16 {margin:0 -640px 1.5em 640px;}
|
||||||
|
.push-17 {margin:0 -680px 1.5em 680px;}
|
||||||
|
.push-18 {margin:0 -720px 1.5em 720px;}
|
||||||
|
.push-19 {margin:0 -760px 1.5em 760px;}
|
||||||
|
.push-20 {margin:0 -800px 1.5em 800px;}
|
||||||
|
.push-21 {margin:0 -840px 1.5em 840px;}
|
||||||
|
.push-22 {margin:0 -880px 1.5em 880px;}
|
||||||
|
.push-23 {margin:0 -920px 1.5em 920px;}
|
||||||
|
.push-24 {margin:0 -960px 1.5em 960px;}
|
||||||
|
.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:left;position:relative;}
|
||||||
|
div.prepend-top, .prepend-top {margin-top:1.5em;}
|
||||||
|
div.append-bottom, .append-bottom {margin-bottom:1.5em;}
|
||||||
|
.box {padding:1.5em;margin-bottom:1.5em;background:#e5eCf9;}
|
||||||
|
hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:1px;margin:0 0 17px;border:none;}
|
||||||
|
hr.space {background:#fff;color:#fff;visibility:hidden;}
|
||||||
|
.clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;}
|
||||||
|
.clearfix, .container {display:block;}
|
||||||
|
.clear {clear:both;}
|
7
framework/cli/views/webapp/hg-hgignore
Normal file
7
framework/cli/views/webapp/hg-hgignore
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
syntax: glob
|
||||||
|
|
||||||
|
syntax: regexp
|
||||||
|
# ignore all except .hgkeep
|
||||||
|
^assets/(?!.*\.hgkeep$).+
|
||||||
|
^protected/runtime/(?!.*\.hgkeep$).+
|
||||||
|
^protected/tests/report/(?!.*\.hgkeep$).+
|
0
framework/cli/views/webapp/images/git-gitkeep
Normal file
0
framework/cli/views/webapp/images/git-gitkeep
Normal file
0
framework/cli/views/webapp/images/hg-hgkeep
Normal file
0
framework/cli/views/webapp/images/hg-hgkeep
Normal file
15
framework/cli/views/webapp/index-test.php
Normal file
15
framework/cli/views/webapp/index-test.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This is the bootstrap file for test application.
|
||||||
|
* This file should be removed when the application is deployed for production.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// change the following paths if necessary
|
||||||
|
$yii=dirname(__FILE__).'/../framework/yii.php';
|
||||||
|
$config=dirname(__FILE__).'/protected/config/test.php';
|
||||||
|
|
||||||
|
// remove the following line when in production mode
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG',true);
|
||||||
|
|
||||||
|
require_once($yii);
|
||||||
|
Yii::createWebApplication($config)->run();
|
13
framework/cli/views/webapp/index.php
Normal file
13
framework/cli/views/webapp/index.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// change the following paths if necessary
|
||||||
|
$yii=dirname(__FILE__).'/../framework/yii.php';
|
||||||
|
$config=dirname(__FILE__).'/protected/config/main.php';
|
||||||
|
|
||||||
|
// remove the following lines when in production mode
|
||||||
|
defined('YII_DEBUG') or define('YII_DEBUG',true);
|
||||||
|
// specify how many levels of call stack should be shown in each log message
|
||||||
|
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
|
||||||
|
|
||||||
|
require_once($yii);
|
||||||
|
Yii::createWebApplication($config)->run();
|
1
framework/cli/views/webapp/protected/.htaccess
Normal file
1
framework/cli/views/webapp/protected/.htaccess
Normal file
@ -0,0 +1 @@
|
|||||||
|
deny from all
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Controller is the customized base controller class.
|
||||||
|
* All controller classes for this application should extend from this base class.
|
||||||
|
*/
|
||||||
|
class Controller extends CController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string the default layout for the controller view. Defaults to '//layouts/column1',
|
||||||
|
* meaning using a single column layout. See 'protected/views/layouts/column1.php'.
|
||||||
|
*/
|
||||||
|
public $layout='//layouts/column1';
|
||||||
|
/**
|
||||||
|
* @var array context menu items. This property will be assigned to {@link CMenu::items}.
|
||||||
|
*/
|
||||||
|
public $menu=array();
|
||||||
|
/**
|
||||||
|
* @var array the breadcrumbs of the current page. The value of this property will
|
||||||
|
* be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
|
||||||
|
* for more details on how to specify this property.
|
||||||
|
*/
|
||||||
|
public $breadcrumbs=array();
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserIdentity represents the data needed to identity a user.
|
||||||
|
* It contains the authentication method that checks if the provided
|
||||||
|
* data can identity the user.
|
||||||
|
*/
|
||||||
|
class UserIdentity extends CUserIdentity
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Authenticates a user.
|
||||||
|
* The example implementation makes sure if the username and password
|
||||||
|
* are both 'demo'.
|
||||||
|
* In practical applications, this should be changed to authenticate
|
||||||
|
* against some persistent user identity storage (e.g. database).
|
||||||
|
* @return boolean whether authentication succeeds.
|
||||||
|
*/
|
||||||
|
public function authenticate()
|
||||||
|
{
|
||||||
|
$users=array(
|
||||||
|
// username => password
|
||||||
|
'demo'=>'demo',
|
||||||
|
'admin'=>'admin',
|
||||||
|
);
|
||||||
|
if(!isset($users[$this->username]))
|
||||||
|
$this->errorCode=self::ERROR_USERNAME_INVALID;
|
||||||
|
elseif($users[$this->username]!==$this->password)
|
||||||
|
$this->errorCode=self::ERROR_PASSWORD_INVALID;
|
||||||
|
else
|
||||||
|
$this->errorCode=self::ERROR_NONE;
|
||||||
|
return !$this->errorCode;
|
||||||
|
}
|
||||||
|
}
|
37
framework/cli/views/webapp/protected/config/console.php
Normal file
37
framework/cli/views/webapp/protected/config/console.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// This is the configuration for yiic console application.
|
||||||
|
// Any writable CConsoleApplication properties can be configured here.
|
||||||
|
return array(
|
||||||
|
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
|
||||||
|
'name'=>'My Console Application',
|
||||||
|
|
||||||
|
// preloading 'log' component
|
||||||
|
'preload'=>array('log'),
|
||||||
|
|
||||||
|
// application components
|
||||||
|
'components'=>array(
|
||||||
|
'db'=>array(
|
||||||
|
'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db',
|
||||||
|
),
|
||||||
|
// uncomment the following to use a MySQL database
|
||||||
|
/*
|
||||||
|
'db'=>array(
|
||||||
|
'connectionString' => 'mysql:host=localhost;dbname=testdrive',
|
||||||
|
'emulatePrepare' => true,
|
||||||
|
'username' => 'root',
|
||||||
|
'password' => '',
|
||||||
|
'charset' => 'utf8',
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
'log'=>array(
|
||||||
|
'class'=>'CLogRouter',
|
||||||
|
'routes'=>array(
|
||||||
|
array(
|
||||||
|
'class'=>'CFileLogRoute',
|
||||||
|
'levels'=>'error, warning',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
90
framework/cli/views/webapp/protected/config/main.php
Normal file
90
framework/cli/views/webapp/protected/config/main.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// uncomment the following to define a path alias
|
||||||
|
// Yii::setPathOfAlias('local','path/to/local-folder');
|
||||||
|
|
||||||
|
// This is the main Web application configuration. Any writable
|
||||||
|
// CWebApplication properties can be configured here.
|
||||||
|
return array(
|
||||||
|
'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
|
||||||
|
'name'=>'My Web Application',
|
||||||
|
|
||||||
|
// preloading 'log' component
|
||||||
|
'preload'=>array('log'),
|
||||||
|
|
||||||
|
// autoloading model and component classes
|
||||||
|
'import'=>array(
|
||||||
|
'application.models.*',
|
||||||
|
'application.components.*',
|
||||||
|
),
|
||||||
|
|
||||||
|
'modules'=>array(
|
||||||
|
// uncomment the following to enable the Gii tool
|
||||||
|
/*
|
||||||
|
'gii'=>array(
|
||||||
|
'class'=>'system.gii.GiiModule',
|
||||||
|
'password'=>'Enter Your Password Here',
|
||||||
|
// If removed, Gii defaults to localhost only. Edit carefully to taste.
|
||||||
|
'ipFilters'=>array('127.0.0.1','::1'),
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
),
|
||||||
|
|
||||||
|
// application components
|
||||||
|
'components'=>array(
|
||||||
|
'user'=>array(
|
||||||
|
// enable cookie-based authentication
|
||||||
|
'allowAutoLogin'=>true,
|
||||||
|
),
|
||||||
|
// uncomment the following to enable URLs in path-format
|
||||||
|
/*
|
||||||
|
'urlManager'=>array(
|
||||||
|
'urlFormat'=>'path',
|
||||||
|
'rules'=>array(
|
||||||
|
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
|
||||||
|
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
|
||||||
|
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
'db'=>array(
|
||||||
|
'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db',
|
||||||
|
),
|
||||||
|
// uncomment the following to use a MySQL database
|
||||||
|
/*
|
||||||
|
'db'=>array(
|
||||||
|
'connectionString' => 'mysql:host=localhost;dbname=testdrive',
|
||||||
|
'emulatePrepare' => true,
|
||||||
|
'username' => 'root',
|
||||||
|
'password' => '',
|
||||||
|
'charset' => 'utf8',
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
'errorHandler'=>array(
|
||||||
|
// use 'site/error' action to display errors
|
||||||
|
'errorAction'=>'site/error',
|
||||||
|
),
|
||||||
|
'log'=>array(
|
||||||
|
'class'=>'CLogRouter',
|
||||||
|
'routes'=>array(
|
||||||
|
array(
|
||||||
|
'class'=>'CFileLogRoute',
|
||||||
|
'levels'=>'error, warning',
|
||||||
|
),
|
||||||
|
// uncomment the following to show log messages on web pages
|
||||||
|
/*
|
||||||
|
array(
|
||||||
|
'class'=>'CWebLogRoute',
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// application-level parameters that can be accessed
|
||||||
|
// using Yii::app()->params['paramName']
|
||||||
|
'params'=>array(
|
||||||
|
// this is used in contact page
|
||||||
|
'adminEmail'=>'webmaster@example.com',
|
||||||
|
),
|
||||||
|
);
|
17
framework/cli/views/webapp/protected/config/test.php
Normal file
17
framework/cli/views/webapp/protected/config/test.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return CMap::mergeArray(
|
||||||
|
require(dirname(__FILE__).'/main.php'),
|
||||||
|
array(
|
||||||
|
'components'=>array(
|
||||||
|
'fixture'=>array(
|
||||||
|
'class'=>'system.test.CDbFixtureManager',
|
||||||
|
),
|
||||||
|
/* uncomment the following to provide test database connection
|
||||||
|
'db'=>array(
|
||||||
|
'connectionString'=>'DSN for test database',
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class SiteController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Declares class-based actions.
|
||||||
|
*/
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
// captcha action renders the CAPTCHA image displayed on the contact page
|
||||||
|
'captcha'=>array(
|
||||||
|
'class'=>'CCaptchaAction',
|
||||||
|
'backColor'=>0xFFFFFF,
|
||||||
|
),
|
||||||
|
// page action renders "static" pages stored under 'protected/views/site/pages'
|
||||||
|
// They can be accessed via: index.php?r=site/page&view=FileName
|
||||||
|
'page'=>array(
|
||||||
|
'class'=>'CViewAction',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the default 'index' action that is invoked
|
||||||
|
* when an action is not explicitly requested by users.
|
||||||
|
*/
|
||||||
|
public function actionIndex()
|
||||||
|
{
|
||||||
|
// renders the view file 'protected/views/site/index.php'
|
||||||
|
// using the default layout 'protected/views/layouts/main.php'
|
||||||
|
$this->render('index');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the action to handle external exceptions.
|
||||||
|
*/
|
||||||
|
public function actionError()
|
||||||
|
{
|
||||||
|
if($error=Yii::app()->errorHandler->error)
|
||||||
|
{
|
||||||
|
if(Yii::app()->request->isAjaxRequest)
|
||||||
|
echo $error['message'];
|
||||||
|
else
|
||||||
|
$this->render('error', $error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the contact page
|
||||||
|
*/
|
||||||
|
public function actionContact()
|
||||||
|
{
|
||||||
|
$model=new ContactForm;
|
||||||
|
if(isset($_POST['ContactForm']))
|
||||||
|
{
|
||||||
|
$model->attributes=$_POST['ContactForm'];
|
||||||
|
if($model->validate())
|
||||||
|
{
|
||||||
|
$name='=?UTF-8?B?'.base64_encode($model->name).'?=';
|
||||||
|
$subject='=?UTF-8?B?'.base64_encode($model->subject).'?=';
|
||||||
|
$headers="From: $name <{$model->email}>\r\n".
|
||||||
|
"Reply-To: {$model->email}\r\n".
|
||||||
|
"MIME-Version: 1.0\r\n".
|
||||||
|
"Content-Type: text/plain; charset=UTF-8";
|
||||||
|
|
||||||
|
mail(Yii::app()->params['adminEmail'],$subject,$model->body,$headers);
|
||||||
|
Yii::app()->user->setFlash('contact','Thank you for contacting us. We will respond to you as soon as possible.');
|
||||||
|
$this->refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->render('contact',array('model'=>$model));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the login page
|
||||||
|
*/
|
||||||
|
public function actionLogin()
|
||||||
|
{
|
||||||
|
$model=new LoginForm;
|
||||||
|
|
||||||
|
// if it is ajax validation request
|
||||||
|
if(isset($_POST['ajax']) && $_POST['ajax']==='login-form')
|
||||||
|
{
|
||||||
|
echo CActiveForm::validate($model);
|
||||||
|
Yii::app()->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect user input data
|
||||||
|
if(isset($_POST['LoginForm']))
|
||||||
|
{
|
||||||
|
$model->attributes=$_POST['LoginForm'];
|
||||||
|
// validate user input and redirect to the previous page if valid
|
||||||
|
if($model->validate() && $model->login())
|
||||||
|
$this->redirect(Yii::app()->user->returnUrl);
|
||||||
|
}
|
||||||
|
// display the login form
|
||||||
|
$this->render('login',array('model'=>$model));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs out the current user and redirect to homepage.
|
||||||
|
*/
|
||||||
|
public function actionLogout()
|
||||||
|
{
|
||||||
|
Yii::app()->user->logout();
|
||||||
|
$this->redirect(Yii::app()->homeUrl);
|
||||||
|
}
|
||||||
|
}
|
28
framework/cli/views/webapp/protected/data/schema.mysql.sql
Normal file
28
framework/cli/views/webapp/protected/data/schema.mysql.sql
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
CREATE TABLE tbl_user (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
username VARCHAR(128) NOT NULL,
|
||||||
|
password VARCHAR(128) NOT NULL,
|
||||||
|
email VARCHAR(128) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test1', 'pass1', 'test1@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test2', 'pass2', 'test2@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test3', 'pass3', 'test3@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test4', 'pass4', 'test4@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test5', 'pass5', 'test5@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test6', 'pass6', 'test6@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test7', 'pass7', 'test7@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test8', 'pass8', 'test8@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test9', 'pass9', 'test9@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test10', 'pass10', 'test10@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test11', 'pass11', 'test11@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test12', 'pass12', 'test12@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test13', 'pass13', 'test13@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test14', 'pass14', 'test14@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test15', 'pass15', 'test15@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test16', 'pass16', 'test16@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test17', 'pass17', 'test17@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test18', 'pass18', 'test18@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test19', 'pass19', 'test19@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test20', 'pass20', 'test20@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test21', 'pass21', 'test21@example.com');
|
28
framework/cli/views/webapp/protected/data/schema.sqlite.sql
Normal file
28
framework/cli/views/webapp/protected/data/schema.sqlite.sql
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
CREATE TABLE tbl_user (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username VARCHAR(128) NOT NULL,
|
||||||
|
password VARCHAR(128) NOT NULL,
|
||||||
|
email VARCHAR(128) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test1', 'pass1', 'test1@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test2', 'pass2', 'test2@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test3', 'pass3', 'test3@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test4', 'pass4', 'test4@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test5', 'pass5', 'test5@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test6', 'pass6', 'test6@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test7', 'pass7', 'test7@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test8', 'pass8', 'test8@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test9', 'pass9', 'test9@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test10', 'pass10', 'test10@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test11', 'pass11', 'test11@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test12', 'pass12', 'test12@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test13', 'pass13', 'test13@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test14', 'pass14', 'test14@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test15', 'pass15', 'test15@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test16', 'pass16', 'test16@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test17', 'pass17', 'test17@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test18', 'pass18', 'test18@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test19', 'pass19', 'test19@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test20', 'pass20', 'test20@example.com');
|
||||||
|
INSERT INTO tbl_user (username, password, email) VALUES ('test21', 'pass21', 'test21@example.com');
|
BIN
framework/cli/views/webapp/protected/data/testdrive.db
Normal file
BIN
framework/cli/views/webapp/protected/data/testdrive.db
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user