@@ -25,3 +25,5 @@ composer.phar | |||
phpunit.phar | |||
# local phpunit config | |||
/phpunit.xml | |||
/vendor/**/.git |
@@ -4,6 +4,7 @@ | |||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", | |||
"This file is @generated automatically" | |||
], | |||
"hash": "0a96d7573d54afab6210cfabf212aae2", | |||
"content-hash": "e287d89eaeb34db8a7f1cc53b4b790b8", | |||
"packages": [ | |||
{ | |||
@@ -64,7 +65,7 @@ | |||
"yii 2", | |||
"yii2" | |||
], | |||
"time": "2016-07-09T19:09:35+00:00" | |||
"time": "2016-07-09 19:09:35" | |||
}, | |||
{ | |||
"name": "2amigos/yii2-leaflet-extension", | |||
@@ -124,7 +125,7 @@ | |||
"yii", | |||
"yii2" | |||
], | |||
"time": "2016-02-05T23:47:38+00:00" | |||
"time": "2016-02-05 23:47:38" | |||
}, | |||
{ | |||
"name": "bower-asset/bootstrap", | |||
@@ -437,7 +438,7 @@ | |||
"markdown", | |||
"markdown-extra" | |||
], | |||
"time": "2016-09-14T20:40:20+00:00" | |||
"time": "2016-09-14 20:40:20" | |||
}, | |||
{ | |||
"name": "ezyang/htmlpurifier", | |||
@@ -481,7 +482,7 @@ | |||
"keywords": [ | |||
"html" | |||
], | |||
"time": "2016-07-16T12:58:58+00:00" | |||
"time": "2016-07-16 12:58:58" | |||
}, | |||
{ | |||
"name": "kartik-v/yii2-mpdf", | |||
@@ -489,12 +490,12 @@ | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/kartik-v/yii2-mpdf.git", | |||
"reference": "c9a888f5755d09bb10a9e5c0675e13565262a1bb" | |||
"reference": "94de4fd901036bb086bf86e10509649345b462a0" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/kartik-v/yii2-mpdf/zipball/c9a888f5755d09bb10a9e5c0675e13565262a1bb", | |||
"reference": "c9a888f5755d09bb10a9e5c0675e13565262a1bb", | |||
"url": "https://api.github.com/repos/kartik-v/yii2-mpdf/zipball/94de4fd901036bb086bf86e10509649345b462a0", | |||
"reference": "94de4fd901036bb086bf86e10509649345b462a0", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
@@ -533,7 +534,7 @@ | |||
"utf8", | |||
"yii2" | |||
], | |||
"time": "2016-09-11 05:41:04" | |||
"time": "2016-11-03 17:25:48" | |||
}, | |||
{ | |||
"name": "mpdf/mpdf", | |||
@@ -584,7 +585,7 @@ | |||
"php", | |||
"utf-8" | |||
], | |||
"time": "2016-07-20T12:31:58+00:00" | |||
"time": "2016-07-20 12:31:58" | |||
}, | |||
{ | |||
"name": "setasign/fpdi", | |||
@@ -633,7 +634,7 @@ | |||
"fpdi", | |||
"pdf" | |||
], | |||
"time": "2015-11-30T10:53:14+00:00" | |||
"time": "2015-11-30 10:53:14" | |||
}, | |||
{ | |||
"name": "swiftmailer/swiftmailer", | |||
@@ -686,20 +687,20 @@ | |||
"mail", | |||
"mailer" | |||
], | |||
"time": "2016-07-08T11:51:25+00:00" | |||
"time": "2016-07-08 11:51:25" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2", | |||
"version": "2.0.9", | |||
"version": "2.0.10", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/yiisoft/yii2-framework.git", | |||
"reference": "2b75151ea60e1fd820046416eee2e89c3dda1133" | |||
"reference": "5bfcb7a6dfa9771e2248eb8c4448613330f343ff" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2b75151ea60e1fd820046416eee2e89c3dda1133", | |||
"reference": "2b75151ea60e1fd820046416eee2e89c3dda1133", | |||
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/5bfcb7a6dfa9771e2248eb8c4448613330f343ff", | |||
"reference": "5bfcb7a6dfa9771e2248eb8c4448613330f343ff", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
@@ -780,7 +781,7 @@ | |||
"framework", | |||
"yii2" | |||
], | |||
"time": "2016-07-11T13:36:42+00:00" | |||
"time": "2016-10-20 12:02:50" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-bootstrap", | |||
@@ -830,7 +831,7 @@ | |||
"bootstrap", | |||
"yii2" | |||
], | |||
"time": "2016-03-17T03:29:28+00:00" | |||
"time": "2016-03-17 03:29:28" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-composer", | |||
@@ -877,7 +878,7 @@ | |||
"extension installer", | |||
"yii2" | |||
], | |||
"time": "2016-02-06T00:49:24+00:00" | |||
"time": "2016-02-06 00:49:24" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-swiftmailer", | |||
@@ -927,7 +928,7 @@ | |||
"swiftmailer", | |||
"yii2" | |||
], | |||
"time": "2016-09-09T11:48:11+00:00" | |||
"time": "2016-09-09 11:48:11" | |||
} | |||
], | |||
"packages-dev": [ | |||
@@ -1004,7 +1005,7 @@ | |||
"faker", | |||
"fixtures" | |||
], | |||
"time": "2016-04-29T12:21:54+00:00" | |||
"time": "2016-04-29 12:21:54" | |||
}, | |||
{ | |||
"name": "phpspec/php-diff", | |||
@@ -1042,7 +1043,7 @@ | |||
} | |||
], | |||
"description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", | |||
"time": "2016-04-07T12:29:16+00:00" | |||
"time": "2016-04-07 12:29:16" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-codeception", | |||
@@ -1087,7 +1088,8 @@ | |||
"codeception", | |||
"yii2" | |||
], | |||
"time": "2016-03-17T03:41:26+00:00" | |||
"abandoned": "codeception/codeception", | |||
"time": "2016-03-17 03:41:26" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-debug", | |||
@@ -1134,7 +1136,7 @@ | |||
"debugger", | |||
"yii2" | |||
], | |||
"time": "2016-03-17T03:50:19+00:00" | |||
"time": "2016-03-17 03:50:19" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-faker", | |||
@@ -1181,7 +1183,7 @@ | |||
"faker", | |||
"yii2" | |||
], | |||
"time": "2015-03-01T06:22:44+00:00" | |||
"time": "2015-03-01 06:22:44" | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-gii", | |||
@@ -1234,7 +1236,7 @@ | |||
"gii", | |||
"yii2" | |||
], | |||
"time": "2016-03-18T14:09:46+00:00" | |||
"time": "2016-03-18 14:09:46" | |||
} | |||
], | |||
"aliases": [], |
@@ -2,6 +2,6 @@ | |||
// autoload.php @generated by Composer | |||
require_once __DIR__ . '/composer/autoload_real.php'; | |||
require_once __DIR__ . '/composer' . '/autoload_real.php'; | |||
return ComposerAutoloaderInitcdf72ff6e7f034c7d87f9bb5bd53f8da::getLoader(); |
@@ -1,4 +1,2 @@ | |||
/.idea/ | |||
composer.lock | |||
/vendor | |||
README.html |
@@ -53,8 +53,8 @@ class ClassLoader | |||
private $useIncludePath = false; | |||
private $classMap = array(); | |||
private $classMapAuthoritative = false; | |||
private $missingClasses = array(); | |||
public function getPrefixes() | |||
{ | |||
@@ -313,24 +313,29 @@ class ClassLoader | |||
*/ | |||
public function findFile($class) | |||
{ | |||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 | |||
if ('\\' == $class[0]) { | |||
$class = substr($class, 1); | |||
} | |||
// class map lookup | |||
if (isset($this->classMap[$class])) { | |||
return $this->classMap[$class]; | |||
} | |||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { | |||
if ($this->classMapAuthoritative) { | |||
return false; | |||
} | |||
$file = $this->findFileWithExtension($class, '.php'); | |||
// Search for Hack files if we are running on HHVM | |||
if (false === $file && defined('HHVM_VERSION')) { | |||
if ($file === null && defined('HHVM_VERSION')) { | |||
$file = $this->findFileWithExtension($class, '.hh'); | |||
} | |||
if (false === $file) { | |||
if ($file === null) { | |||
// Remember that this class does not exist. | |||
$this->missingClasses[$class] = true; | |||
return $this->classMap[$class] = false; | |||
} | |||
return $file; | |||
@@ -394,8 +399,6 @@ class ClassLoader | |||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { | |||
return $file; | |||
} | |||
return false; | |||
} | |||
} | |||
@@ -1,21 +1,433 @@ | |||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||
Upstream-Name: Composer | |||
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be> | |||
Source: https://github.com/composer/composer | |||
Copyright (c) 2016 Nils Adermann, Jordi Boggiano | |||
Files: * | |||
Copyright: 2016, Nils Adermann <naderman@naderman.de> | |||
2016, Jordi Boggiano <j.boggiano@seld.be> | |||
License: Expat | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is furnished | |||
to do so, subject to the following conditions: | |||
Files: res/cacert.pem | |||
Copyright: 2015, Mozilla Foundation | |||
License: MPL-2.0 | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
Files: src/Composer/Util/RemoteFilesystem.php | |||
src/Composer/Util/TlsHelper.php | |||
Copyright: 2016, Nils Adermann <naderman@naderman.de> | |||
2016, Jordi Boggiano <j.boggiano@seld.be> | |||
2013, Evan Coury <me@evancoury.com> | |||
License: Expat and BSD-2-Clause | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
License: BSD-2-Clause | |||
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. | |||
. | |||
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. | |||
License: Expat | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is furnished | |||
to do so, subject to the following conditions: | |||
. | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
License: MPL-2.0 | |||
1. Definitions | |||
-------------- | |||
. | |||
1.1. "Contributor" | |||
means each individual or legal entity that creates, contributes to | |||
the creation of, or owns Covered Software. | |||
. | |||
1.2. "Contributor Version" | |||
means the combination of the Contributions of others (if any) used | |||
by a Contributor and that particular Contributor's Contribution. | |||
. | |||
1.3. "Contribution" | |||
means Covered Software of a particular Contributor. | |||
. | |||
1.4. "Covered Software" | |||
means Source Code Form to which the initial Contributor has attached | |||
the notice in Exhibit A, the Executable Form of such Source Code | |||
Form, and Modifications of such Source Code Form, in each case | |||
including portions thereof. | |||
. | |||
1.5. "Incompatible With Secondary Licenses" | |||
means | |||
. | |||
(a) that the initial Contributor has attached the notice described | |||
in Exhibit B to the Covered Software; or | |||
. | |||
(b) that the Covered Software was made available under the terms of | |||
version 1.1 or earlier of the License, but not also under the | |||
terms of a Secondary License. | |||
. | |||
1.6. "Executable Form" | |||
means any form of the work other than Source Code Form. | |||
. | |||
1.7. "Larger Work" | |||
means a work that combines Covered Software with other material, in | |||
a separate file or files, that is not Covered Software. | |||
. | |||
1.8. "License" | |||
means this document. | |||
. | |||
1.9. "Licensable" | |||
means having the right to grant, to the maximum extent possible, | |||
whether at the time of the initial grant or subsequently, any and | |||
all of the rights conveyed by this License. | |||
. | |||
1.10. "Modifications" | |||
means any of the following: | |||
. | |||
(a) any file in Source Code Form that results from an addition to, | |||
deletion from, or modification of the contents of Covered | |||
Software; or | |||
. | |||
(b) any new file in Source Code Form that contains any Covered | |||
Software. | |||
. | |||
1.11. "Patent Claims" of a Contributor | |||
means any patent claim(s), including without limitation, method, | |||
process, and apparatus claims, in any patent Licensable by such | |||
Contributor that would be infringed, but for the grant of the | |||
License, by the making, using, selling, offering for sale, having | |||
made, import, or transfer of either its Contributions or its | |||
Contributor Version. | |||
. | |||
1.12. "Secondary License" | |||
means either the GNU General Public License, Version 2.0, the GNU | |||
Lesser General Public License, Version 2.1, the GNU Affero General | |||
Public License, Version 3.0, or any later versions of those | |||
licenses. | |||
. | |||
1.13. "Source Code Form" | |||
means the form of the work preferred for making modifications. | |||
. | |||
1.14. "You" (or "Your") | |||
means an individual or a legal entity exercising rights under this | |||
License. For legal entities, "You" includes any entity that | |||
controls, is controlled by, or is under common control with You. For | |||
purposes of this definition, "control" means (a) the power, direct | |||
or indirect, to cause the direction or management of such entity, | |||
whether by contract or otherwise, or (b) ownership of more than | |||
fifty percent (50%) of the outstanding shares or beneficial | |||
ownership of such entity. | |||
. | |||
2. License Grants and Conditions | |||
-------------------------------- | |||
. | |||
2.1. Grants | |||
. | |||
Each Contributor hereby grants You a world-wide, royalty-free, | |||
non-exclusive license: | |||
. | |||
(a) under intellectual property rights (other than patent or trademark) | |||
Licensable by such Contributor to use, reproduce, make available, | |||
modify, display, perform, distribute, and otherwise exploit its | |||
Contributions, either on an unmodified basis, with Modifications, or | |||
as part of a Larger Work; and | |||
. | |||
(b) under Patent Claims of such Contributor to make, use, sell, offer | |||
for sale, have made, import, and otherwise transfer either its | |||
Contributions or its Contributor Version. | |||
. | |||
2.2. Effective Date | |||
. | |||
The licenses granted in Section 2.1 with respect to any Contribution | |||
become effective for each Contribution on the date the Contributor first | |||
distributes such Contribution. | |||
. | |||
2.3. Limitations on Grant Scope | |||
. | |||
The licenses granted in this Section 2 are the only rights granted under | |||
this License. No additional rights or licenses will be implied from the | |||
distribution or licensing of Covered Software under this License. | |||
Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
Contributor: | |||
. | |||
(a) for any code that a Contributor has removed from Covered Software; | |||
or | |||
. | |||
(b) for infringements caused by: (i) Your and any other third party's | |||
modifications of Covered Software, or (ii) the combination of its | |||
Contributions with other software (except as part of its Contributor | |||
Version); or | |||
. | |||
(c) under Patent Claims infringed by Covered Software in the absence of | |||
its Contributions. | |||
. | |||
This License does not grant any rights in the trademarks, service marks, | |||
or logos of any Contributor (except as may be necessary to comply with | |||
the notice requirements in Section 3.4). | |||
. | |||
2.4. Subsequent Licenses | |||
. | |||
No Contributor makes additional grants as a result of Your choice to | |||
distribute the Covered Software under a subsequent version of this | |||
License (see Section 10.2) or under the terms of a Secondary License (if | |||
permitted under the terms of Section 3.3). | |||
. | |||
2.5. Representation | |||
. | |||
Each Contributor represents that the Contributor believes its | |||
Contributions are its original creation(s) or it has sufficient rights | |||
to grant the rights to its Contributions conveyed by this License. | |||
. | |||
2.6. Fair Use | |||
. | |||
This License is not intended to limit any rights You have under | |||
applicable copyright doctrines of fair use, fair dealing, or other | |||
equivalents. | |||
. | |||
2.7. Conditions | |||
. | |||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | |||
in Section 2.1. | |||
. | |||
3. Responsibilities | |||
------------------- | |||
. | |||
3.1. Distribution of Source Form | |||
. | |||
All distribution of Covered Software in Source Code Form, including any | |||
Modifications that You create or to which You contribute, must be under | |||
the terms of this License. You must inform recipients that the Source | |||
Code Form of the Covered Software is governed by the terms of this | |||
License, and how they can obtain a copy of this License. You may not | |||
attempt to alter or restrict the recipients' rights in the Source Code | |||
Form. | |||
. | |||
3.2. Distribution of Executable Form | |||
. | |||
If You distribute Covered Software in Executable Form then: | |||
. | |||
(a) such Covered Software must also be made available in Source Code | |||
Form, as described in Section 3.1, and You must inform recipients of | |||
the Executable Form how they can obtain a copy of such Source Code | |||
Form by reasonable means in a timely manner, at a charge no more | |||
than the cost of distribution to the recipient; and | |||
. | |||
(b) You may distribute such Executable Form under the terms of this | |||
License, or sublicense it under different terms, provided that the | |||
license for the Executable Form does not attempt to limit or alter | |||
the recipients' rights in the Source Code Form under this License. | |||
. | |||
3.3. Distribution of a Larger Work | |||
. | |||
You may create and distribute a Larger Work under terms of Your choice, | |||
provided that You also comply with the requirements of this License for | |||
the Covered Software. If the Larger Work is a combination of Covered | |||
Software with a work governed by one or more Secondary Licenses, and the | |||
Covered Software is not Incompatible With Secondary Licenses, this | |||
License permits You to additionally distribute such Covered Software | |||
under the terms of such Secondary License(s), so that the recipient of | |||
the Larger Work may, at their option, further distribute the Covered | |||
Software under the terms of either this License or such Secondary | |||
License(s). | |||
. | |||
3.4. Notices | |||
. | |||
You may not remove or alter the substance of any license notices | |||
(including copyright notices, patent notices, disclaimers of warranty, | |||
or limitations of liability) contained within the Source Code Form of | |||
the Covered Software, except that You may alter any license notices to | |||
the extent required to remedy known factual inaccuracies. | |||
. | |||
3.5. Application of Additional Terms | |||
. | |||
You may choose to offer, and to charge a fee for, warranty, support, | |||
indemnity or liability obligations to one or more recipients of Covered | |||
Software. However, You may do so only on Your own behalf, and not on | |||
behalf of any Contributor. You must make it absolutely clear that any | |||
such warranty, support, indemnity, or liability obligation is offered by | |||
You alone, and You hereby agree to indemnify every Contributor for any | |||
liability incurred by such Contributor as a result of warranty, support, | |||
indemnity or liability terms You offer. You may include additional | |||
disclaimers of warranty and limitations of liability specific to any | |||
jurisdiction. | |||
. | |||
4. Inability to Comply Due to Statute or Regulation | |||
--------------------------------------------------- | |||
. | |||
If it is impossible for You to comply with any of the terms of this | |||
License with respect to some or all of the Covered Software due to | |||
statute, judicial order, or regulation then You must: (a) comply with | |||
the terms of this License to the maximum extent possible; and (b) | |||
describe the limitations and the code they affect. Such description must | |||
be placed in a text file included with all distributions of the Covered | |||
Software under this License. Except to the extent prohibited by statute | |||
or regulation, such description must be sufficiently detailed for a | |||
recipient of ordinary skill to be able to understand it. | |||
. | |||
5. Termination | |||
-------------- | |||
. | |||
5.1. The rights granted under this License will terminate automatically | |||
if You fail to comply with any of its terms. However, if You become | |||
compliant, then the rights granted under this License from a particular | |||
Contributor are reinstated (a) provisionally, unless and until such | |||
Contributor explicitly and finally terminates Your grants, and (b) on an | |||
ongoing basis, if such Contributor fails to notify You of the | |||
non-compliance by some reasonable means prior to 60 days after You have | |||
come back into compliance. Moreover, Your grants from a particular | |||
Contributor are reinstated on an ongoing basis if such Contributor | |||
notifies You of the non-compliance by some reasonable means, this is the | |||
first time You have received notice of non-compliance with this License | |||
from such Contributor, and You become compliant prior to 30 days after | |||
Your receipt of the notice. | |||
. | |||
5.2. If You initiate litigation against any entity by asserting a patent | |||
infringement claim (excluding declaratory judgment actions, | |||
counter-claims, and cross-claims) alleging that a Contributor Version | |||
directly or indirectly infringes any patent, then the rights granted to | |||
You by any and all Contributors for the Covered Software under Section | |||
2.1 of this License shall terminate. | |||
. | |||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all | |||
end user license agreements (excluding distributors and resellers) which | |||
have been validly granted by You or Your distributors under this License | |||
prior to termination shall survive termination. | |||
. | |||
************************************************************************ | |||
* * | |||
* 6. Disclaimer of Warranty * | |||
* ------------------------- * | |||
* * | |||
* Covered Software is provided under this License on an "as is" * | |||
* basis, without warranty of any kind, either expressed, implied, or * | |||
* statutory, including, without limitation, warranties that the * | |||
* Covered Software is free of defects, merchantable, fit for a * | |||
* particular purpose or non-infringing. The entire risk as to the * | |||
* quality and performance of the Covered Software is with You. * | |||
* Should any Covered Software prove defective in any respect, You * | |||
* (not any Contributor) assume the cost of any necessary servicing, * | |||
* repair, or correction. This disclaimer of warranty constitutes an * | |||
* essential part of this License. No use of any Covered Software is * | |||
* authorized under this License except under this disclaimer. * | |||
* * | |||
************************************************************************ | |||
. | |||
************************************************************************ | |||
* * | |||
* 7. Limitation of Liability * | |||
* -------------------------- * | |||
* * | |||
* Under no circumstances and under no legal theory, whether tort * | |||
* (including negligence), contract, or otherwise, shall any * | |||
* Contributor, or anyone who distributes Covered Software as * | |||
* permitted above, be liable to You for any direct, indirect, * | |||
* special, incidental, or consequential damages of any character * | |||
* including, without limitation, damages for lost profits, loss of * | |||
* goodwill, work stoppage, computer failure or malfunction, or any * | |||
* and all other commercial damages or losses, even if such party * | |||
* shall have been informed of the possibility of such damages. This * | |||
* limitation of liability shall not apply to liability for death or * | |||
* personal injury resulting from such party's negligence to the * | |||
* extent applicable law prohibits such limitation. Some * | |||
* jurisdictions do not allow the exclusion or limitation of * | |||
* incidental or consequential damages, so this exclusion and * | |||
* limitation may not apply to You. * | |||
* * | |||
************************************************************************ | |||
. | |||
8. Litigation | |||
------------- | |||
. | |||
Any litigation relating to this License may be brought only in the | |||
courts of a jurisdiction where the defendant maintains its principal | |||
place of business and such litigation shall be governed by laws of that | |||
jurisdiction, without reference to its conflict-of-law provisions. | |||
Nothing in this Section shall prevent a party's ability to bring | |||
cross-claims or counter-claims. | |||
. | |||
9. Miscellaneous | |||
---------------- | |||
. | |||
This License represents the complete agreement concerning the subject | |||
matter hereof. If any provision of this License is held to be | |||
unenforceable, such provision shall be reformed only to the extent | |||
necessary to make it enforceable. Any law or regulation which provides | |||
that the language of a contract shall be construed against the drafter | |||
shall not be used to construe this License against a Contributor. | |||
. | |||
10. Versions of the License | |||
--------------------------- | |||
. | |||
10.1. New Versions | |||
. | |||
Mozilla Foundation is the license steward. Except as provided in Section | |||
10.3, no one other than the license steward has the right to modify or | |||
publish new versions of this License. Each version will be given a | |||
distinguishing version number. | |||
. | |||
10.2. Effect of New Versions | |||
. | |||
You may distribute the Covered Software under the terms of the version | |||
of the License under which You originally received the Covered Software, | |||
or under the terms of any subsequent version published by the license | |||
steward. | |||
. | |||
10.3. Modified Versions | |||
. | |||
If you create software not governed by this License, and you want to | |||
create a new license for such software, you may create and use a | |||
modified version of this License if you rename the license and remove | |||
any references to the name of the license steward (except to note that | |||
such modified license differs from this License). | |||
. | |||
10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
Licenses | |||
. | |||
If You choose to distribute Source Code Form that is Incompatible With | |||
Secondary Licenses under the terms of this version of the License, the | |||
notice described in Exhibit B of this License must be attached. | |||
. | |||
Exhibit A - Source Code Form License Notice | |||
------------------------------------------- | |||
. | |||
This Source Code Form is subject to the terms of the Mozilla Public | |||
License, v. 2.0. If a copy of the MPL was not distributed with this | |||
file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
. | |||
If it is not possible or desirable to put the notice in a particular | |||
file, then You may include the notice in a location (such as a LICENSE | |||
file in a relevant directory) where a recipient would be likely to look | |||
for such a notice. | |||
. | |||
You may add additional accurate notices of copyright ownership. | |||
. | |||
Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
--------------------------------------------------------- | |||
. | |||
This Source Code Form is "Incompatible With Secondary Licenses", as | |||
defined by the Mozilla Public License, v. 2.0. |
@@ -23,35 +23,24 @@ class ComposerAutoloaderInitcdf72ff6e7f034c7d87f9bb5bd53f8da | |||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); | |||
spl_autoload_unregister(array('ComposerAutoloaderInitcdf72ff6e7f034c7d87f9bb5bd53f8da', 'loadClassLoader')); | |||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION'); | |||
if ($useStaticLoader) { | |||
require_once __DIR__ . '/autoload_static.php'; | |||
call_user_func(\Composer\Autoload\ComposerStaticInitcdf72ff6e7f034c7d87f9bb5bd53f8da::getInitializer($loader)); | |||
} else { | |||
$map = require __DIR__ . '/autoload_namespaces.php'; | |||
foreach ($map as $namespace => $path) { | |||
$loader->set($namespace, $path); | |||
} | |||
$map = require __DIR__ . '/autoload_namespaces.php'; | |||
foreach ($map as $namespace => $path) { | |||
$loader->set($namespace, $path); | |||
} | |||
$map = require __DIR__ . '/autoload_psr4.php'; | |||
foreach ($map as $namespace => $path) { | |||
$loader->setPsr4($namespace, $path); | |||
} | |||
$map = require __DIR__ . '/autoload_psr4.php'; | |||
foreach ($map as $namespace => $path) { | |||
$loader->setPsr4($namespace, $path); | |||
} | |||
$classMap = require __DIR__ . '/autoload_classmap.php'; | |||
if ($classMap) { | |||
$loader->addClassMap($classMap); | |||
} | |||
$classMap = require __DIR__ . '/autoload_classmap.php'; | |||
if ($classMap) { | |||
$loader->addClassMap($classMap); | |||
} | |||
$loader->register(true); | |||
if ($useStaticLoader) { | |||
$includeFiles = Composer\Autoload\ComposerStaticInitcdf72ff6e7f034c7d87f9bb5bd53f8da::$files; | |||
} else { | |||
$includeFiles = require __DIR__ . '/autoload_files.php'; | |||
} | |||
$includeFiles = require __DIR__ . '/autoload_files.php'; | |||
foreach ($includeFiles as $fileIdentifier => $file) { | |||
composerRequirecdf72ff6e7f034c7d87f9bb5bd53f8da($fileIdentifier, $file); | |||
} |
@@ -18,7 +18,7 @@ | |||
"fzaninotto/faker": "*", | |||
"yiisoft/yii2": "*" | |||
}, | |||
"time": "2015-03-01T06:22:44+00:00", | |||
"time": "2015-03-01 06:22:44", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -96,7 +96,7 @@ | |||
"require": { | |||
"composer-plugin-api": "^1.0" | |||
}, | |||
"time": "2016-02-06T00:49:24+00:00", | |||
"time": "2016-02-06 00:49:24", | |||
"type": "composer-plugin", | |||
"extra": { | |||
"class": "yii\\composer\\Plugin", | |||
@@ -148,7 +148,7 @@ | |||
"require-dev": { | |||
"mockery/mockery": "~0.9.1" | |||
}, | |||
"time": "2016-07-08T11:51:25+00:00", | |||
"time": "2016-07-08 11:51:25", | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -323,7 +323,7 @@ | |||
"facebook/xhprof": "*@dev", | |||
"phpunit/phpunit": "4.1.*" | |||
}, | |||
"time": "2016-09-14T20:40:20+00:00", | |||
"time": "2016-09-14 20:40:20", | |||
"bin": [ | |||
"bin/markdown" | |||
], | |||
@@ -379,7 +379,7 @@ | |||
"require": { | |||
"php": ">=5.2" | |||
}, | |||
"time": "2016-07-16T12:58:58+00:00", | |||
"time": "2016-07-16 12:58:58", | |||
"type": "library", | |||
"installation-source": "dist", | |||
"autoload": { | |||
@@ -407,102 +407,6 @@ | |||
"html" | |||
] | |||
}, | |||
{ | |||
"name": "yiisoft/yii2", | |||
"version": "2.0.9", | |||
"version_normalized": "2.0.9.0", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/yiisoft/yii2-framework.git", | |||
"reference": "2b75151ea60e1fd820046416eee2e89c3dda1133" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2b75151ea60e1fd820046416eee2e89c3dda1133", | |||
"reference": "2b75151ea60e1fd820046416eee2e89c3dda1133", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", | |||
"bower-asset/jquery.inputmask": "~3.2.2", | |||
"bower-asset/punycode": "1.3.*", | |||
"bower-asset/yii2-pjax": "~2.0.1", | |||
"cebe/markdown": "~1.0.0 | ~1.1.0", | |||
"ext-ctype": "*", | |||
"ext-mbstring": "*", | |||
"ezyang/htmlpurifier": "~4.6", | |||
"lib-pcre": "*", | |||
"php": ">=5.4.0", | |||
"yiisoft/yii2-composer": "~2.0.4" | |||
}, | |||
"time": "2016-07-11T13:36:42+00:00", | |||
"bin": [ | |||
"yii" | |||
], | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": { | |||
"dev-master": "2.0.x-dev" | |||
} | |||
}, | |||
"installation-source": "dist", | |||
"autoload": { | |||
"psr-4": { | |||
"yii\\": "" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"BSD-3-Clause" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Qiang Xue", | |||
"email": "qiang.xue@gmail.com", | |||
"homepage": "http://www.yiiframework.com/", | |||
"role": "Founder and project lead" | |||
}, | |||
{ | |||
"name": "Alexander Makarov", | |||
"email": "sam@rmcreative.ru", | |||
"homepage": "http://rmcreative.ru/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Maurizio Domba", | |||
"homepage": "http://mdomba.info/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Carsten Brandt", | |||
"email": "mail@cebe.cc", | |||
"homepage": "http://cebe.cc/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Timur Ruziev", | |||
"email": "resurtm@gmail.com", | |||
"homepage": "http://resurtm.com/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Paul Klimov", | |||
"email": "klimov.paul@gmail.com", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Dmitry Naumenko", | |||
"email": "d.naumenko.a@gmail.com", | |||
"role": "Core framework development" | |||
} | |||
], | |||
"description": "Yii PHP Framework Version 2", | |||
"homepage": "http://www.yiiframework.com/", | |||
"keywords": [ | |||
"framework", | |||
"yii2" | |||
] | |||
}, | |||
{ | |||
"name": "yiisoft/yii2-swiftmailer", | |||
"version": "2.0.6", | |||
@@ -522,7 +426,7 @@ | |||
"swiftmailer/swiftmailer": "~5.0", | |||
"yiisoft/yii2": "~2.0.4" | |||
}, | |||
"time": "2016-09-09T11:48:11+00:00", | |||
"time": "2016-09-09 11:48:11", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -607,7 +511,7 @@ | |||
"require-dev": { | |||
"phpunit/phpunit": "4.*" | |||
}, | |||
"time": "2016-07-09T19:09:35+00:00", | |||
"time": "2016-07-09 19:09:35", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -665,7 +569,7 @@ | |||
"require": { | |||
"yiisoft/yii2": ">=2.0.4" | |||
}, | |||
"time": "2016-03-17T03:41:26+00:00", | |||
"time": "2016-03-17 03:41:26", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -765,7 +669,7 @@ | |||
"bower-asset/bootstrap": "3.3.* | 3.2.* | 3.1.*", | |||
"yiisoft/yii2": ">=2.0.6" | |||
}, | |||
"time": "2016-03-17T03:29:28+00:00", | |||
"time": "2016-03-17 03:29:28", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -817,7 +721,7 @@ | |||
"yiisoft/yii2": ">=2.0.4", | |||
"yiisoft/yii2-bootstrap": "*" | |||
}, | |||
"time": "2016-03-17T03:50:19+00:00", | |||
"time": "2016-03-17 03:50:19", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -891,7 +795,7 @@ | |||
"reference": "0464787bfa7cd13576c5a1e318709768798bec6a", | |||
"shasum": "" | |||
}, | |||
"time": "2016-04-07T12:29:16+00:00", | |||
"time": "2016-04-07 12:29:16", | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -937,7 +841,7 @@ | |||
"yiisoft/yii2": ">=2.0.4", | |||
"yiisoft/yii2-bootstrap": "~2.0" | |||
}, | |||
"time": "2016-03-18T14:09:46+00:00", | |||
"time": "2016-03-18 14:09:46", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -994,7 +898,7 @@ | |||
"phpunit/phpunit": "~4.0", | |||
"squizlabs/php_codesniffer": "~1.5" | |||
}, | |||
"time": "2016-04-29T12:21:54+00:00", | |||
"time": "2016-04-29 12:21:54", | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": [] | |||
@@ -1082,7 +986,7 @@ | |||
"require-dev": { | |||
"phpunit/phpunit": "4.*" | |||
}, | |||
"time": "2016-02-05T23:47:38+00:00", | |||
"time": "2016-02-05 23:47:38", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { | |||
@@ -1142,7 +1046,7 @@ | |||
"setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.", | |||
"setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF." | |||
}, | |||
"time": "2015-11-30T10:53:14+00:00", | |||
"time": "2015-11-30 10:53:14", | |||
"type": "library", | |||
"installation-source": "dist", | |||
"autoload": { | |||
@@ -1199,7 +1103,7 @@ | |||
"suggest": { | |||
"ext-zlib": "Needed for compression of embedded resources, such as fonts" | |||
}, | |||
"time": "2016-07-20T12:31:58+00:00", | |||
"time": "2016-07-20 12:31:58", | |||
"type": "library", | |||
"installation-source": "dist", | |||
"autoload": { | |||
@@ -1226,6 +1130,102 @@ | |||
"utf-8" | |||
] | |||
}, | |||
{ | |||
"name": "yiisoft/yii2", | |||
"version": "2.0.10", | |||
"version_normalized": "2.0.10.0", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/yiisoft/yii2-framework.git", | |||
"reference": "5bfcb7a6dfa9771e2248eb8c4448613330f343ff" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/5bfcb7a6dfa9771e2248eb8c4448613330f343ff", | |||
"reference": "5bfcb7a6dfa9771e2248eb8c4448613330f343ff", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"bower-asset/jquery": "2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", | |||
"bower-asset/jquery.inputmask": "~3.2.2", | |||
"bower-asset/punycode": "1.3.*", | |||
"bower-asset/yii2-pjax": "~2.0.1", | |||
"cebe/markdown": "~1.0.0 | ~1.1.0", | |||
"ext-ctype": "*", | |||
"ext-mbstring": "*", | |||
"ezyang/htmlpurifier": "~4.6", | |||
"lib-pcre": "*", | |||
"php": ">=5.4.0", | |||
"yiisoft/yii2-composer": "~2.0.4" | |||
}, | |||
"time": "2016-10-20 12:02:50", | |||
"bin": [ | |||
"yii" | |||
], | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": { | |||
"dev-master": "2.0.x-dev" | |||
} | |||
}, | |||
"installation-source": "dist", | |||
"autoload": { | |||
"psr-4": { | |||
"yii\\": "" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"BSD-3-Clause" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Qiang Xue", | |||
"email": "qiang.xue@gmail.com", | |||
"homepage": "http://www.yiiframework.com/", | |||
"role": "Founder and project lead" | |||
}, | |||
{ | |||
"name": "Alexander Makarov", | |||
"email": "sam@rmcreative.ru", | |||
"homepage": "http://rmcreative.ru/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Maurizio Domba", | |||
"homepage": "http://mdomba.info/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Carsten Brandt", | |||
"email": "mail@cebe.cc", | |||
"homepage": "http://cebe.cc/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Timur Ruziev", | |||
"email": "resurtm@gmail.com", | |||
"homepage": "http://resurtm.com/", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Paul Klimov", | |||
"email": "klimov.paul@gmail.com", | |||
"role": "Core framework development" | |||
}, | |||
{ | |||
"name": "Dmitry Naumenko", | |||
"email": "d.naumenko.a@gmail.com", | |||
"role": "Core framework development" | |||
} | |||
], | |||
"description": "Yii PHP Framework Version 2", | |||
"homepage": "http://www.yiiframework.com/", | |||
"keywords": [ | |||
"framework", | |||
"yii2" | |||
] | |||
}, | |||
{ | |||
"name": "kartik-v/yii2-mpdf", | |||
"version": "dev-master", | |||
@@ -1233,18 +1233,18 @@ | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/kartik-v/yii2-mpdf.git", | |||
"reference": "c9a888f5755d09bb10a9e5c0675e13565262a1bb" | |||
"reference": "94de4fd901036bb086bf86e10509649345b462a0" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/kartik-v/yii2-mpdf/zipball/c9a888f5755d09bb10a9e5c0675e13565262a1bb", | |||
"reference": "c9a888f5755d09bb10a9e5c0675e13565262a1bb", | |||
"url": "https://api.github.com/repos/kartik-v/yii2-mpdf/zipball/94de4fd901036bb086bf86e10509649345b462a0", | |||
"reference": "94de4fd901036bb086bf86e10509649345b462a0", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"mpdf/mpdf": "<7.0" | |||
}, | |||
"time": "2016-09-11T05:41:04+00:00", | |||
"time": "2016-11-03 17:25:48", | |||
"type": "yii2-extension", | |||
"extra": { | |||
"branch-alias": { |
@@ -0,0 +1,504 @@ | |||
GNU LESSER GENERAL PUBLIC LICENSE | |||
Version 2.1, February 1999 | |||
Copyright (C) 1991, 1999 Free Software Foundation, Inc. | |||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Everyone is permitted to copy and distribute verbatim copies | |||
of this license document, but changing it is not allowed. | |||
[This is the first released version of the Lesser GPL. It also counts | |||
as the successor of the GNU Library Public License, version 2, hence | |||
the version number 2.1.] | |||
Preamble | |||
The licenses for most software are designed to take away your | |||
freedom to share and change it. By contrast, the GNU General Public | |||
Licenses are intended to guarantee your freedom to share and change | |||
free software--to make sure the software is free for all its users. | |||
This license, the Lesser General Public License, applies to some | |||
specially designated software packages--typically libraries--of the | |||
Free Software Foundation and other authors who decide to use it. You | |||
can use it too, but we suggest you first think carefully about whether | |||
this license or the ordinary General Public License is the better | |||
strategy to use in any particular case, based on the explanations below. | |||
When we speak of free software, we are referring to freedom of use, | |||
not price. Our General Public Licenses are designed to make sure that | |||
you have the freedom to distribute copies of free software (and charge | |||
for this service if you wish); that you receive source code or can get | |||
it if you want it; that you can change the software and use pieces of | |||
it in new free programs; and that you are informed that you can do | |||
these things. | |||
To protect your rights, we need to make restrictions that forbid | |||
distributors to deny you these rights or to ask you to surrender these | |||
rights. These restrictions translate to certain responsibilities for | |||
you if you distribute copies of the library or if you modify it. | |||
For example, if you distribute copies of the library, whether gratis | |||
or for a fee, you must give the recipients all the rights that we gave | |||
you. You must make sure that they, too, receive or can get the source | |||
code. If you link other code with the library, you must provide | |||
complete object files to the recipients, so that they can relink them | |||
with the library after making changes to the library and recompiling | |||
it. And you must show them these terms so they know their rights. | |||
We protect your rights with a two-step method: (1) we copyright the | |||
library, and (2) we offer you this license, which gives you legal | |||
permission to copy, distribute and/or modify the library. | |||
To protect each distributor, we want to make it very clear that | |||
there is no warranty for the free library. Also, if the library is | |||
modified by someone else and passed on, the recipients should know | |||
that what they have is not the original version, so that the original | |||
author's reputation will not be affected by problems that might be | |||
introduced by others. | |||
Finally, software patents pose a constant threat to the existence of | |||
any free program. We wish to make sure that a company cannot | |||
effectively restrict the users of a free program by obtaining a | |||
restrictive license from a patent holder. Therefore, we insist that | |||
any patent license obtained for a version of the library must be | |||
consistent with the full freedom of use specified in this license. | |||
Most GNU software, including some libraries, is covered by the | |||
ordinary GNU General Public License. This license, the GNU Lesser | |||
General Public License, applies to certain designated libraries, and | |||
is quite different from the ordinary General Public License. We use | |||
this license for certain libraries in order to permit linking those | |||
libraries into non-free programs. | |||
When a program is linked with a library, whether statically or using | |||
a shared library, the combination of the two is legally speaking a | |||
combined work, a derivative of the original library. The ordinary | |||
General Public License therefore permits such linking only if the | |||
entire combination fits its criteria of freedom. The Lesser General | |||
Public License permits more lax criteria for linking other code with | |||
the library. | |||
We call this license the "Lesser" General Public License because it | |||
does Less to protect the user's freedom than the ordinary General | |||
Public License. It also provides other free software developers Less | |||
of an advantage over competing non-free programs. These disadvantages | |||
are the reason we use the ordinary General Public License for many | |||
libraries. However, the Lesser license provides advantages in certain | |||
special circumstances. | |||
For example, on rare occasions, there may be a special need to | |||
encourage the widest possible use of a certain library, so that it becomes | |||
a de-facto standard. To achieve this, non-free programs must be | |||
allowed to use the library. A more frequent case is that a free | |||
library does the same job as widely used non-free libraries. In this | |||
case, there is little to gain by limiting the free library to free | |||
software only, so we use the Lesser General Public License. | |||
In other cases, permission to use a particular library in non-free | |||
programs enables a greater number of people to use a large body of | |||
free software. For example, permission to use the GNU C Library in | |||
non-free programs enables many more people to use the whole GNU | |||
operating system, as well as its variant, the GNU/Linux operating | |||
system. | |||
Although the Lesser General Public License is Less protective of the | |||
users' freedom, it does ensure that the user of a program that is | |||
linked with the Library has the freedom and the wherewithal to run | |||
that program using a modified version of the Library. | |||
The precise terms and conditions for copying, distribution and | |||
modification follow. Pay close attention to the difference between a | |||
"work based on the library" and a "work that uses the library". The | |||
former contains code derived from the library, whereas the latter must | |||
be combined with the library in order to run. | |||
GNU LESSER GENERAL PUBLIC LICENSE | |||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
0. This License Agreement applies to any software library or other | |||
program which contains a notice placed by the copyright holder or | |||
other authorized party saying it may be distributed under the terms of | |||
this Lesser General Public License (also called "this License"). | |||
Each licensee is addressed as "you". | |||
A "library" means a collection of software functions and/or data | |||
prepared so as to be conveniently linked with application programs | |||
(which use some of those functions and data) to form executables. | |||
The "Library", below, refers to any such software library or work | |||
which has been distributed under these terms. A "work based on the | |||
Library" means either the Library or any derivative work under | |||
copyright law: that is to say, a work containing the Library or a | |||
portion of it, either verbatim or with modifications and/or translated | |||
straightforwardly into another language. (Hereinafter, translation is | |||
included without limitation in the term "modification".) | |||
"Source code" for a work means the preferred form of the work for | |||
making modifications to it. For a library, complete source code means | |||
all the source code for all modules it contains, plus any associated | |||
interface definition files, plus the scripts used to control compilation | |||
and installation of the library. | |||
Activities other than copying, distribution and modification are not | |||
covered by this License; they are outside its scope. The act of | |||
running a program using the Library is not restricted, and output from | |||
such a program is covered only if its contents constitute a work based | |||
on the Library (independent of the use of the Library in a tool for | |||
writing it). Whether that is true depends on what the Library does | |||
and what the program that uses the Library does. | |||
1. You may copy and distribute verbatim copies of the Library's | |||
complete source code as you receive it, in any medium, provided that | |||
you conspicuously and appropriately publish on each copy an | |||
appropriate copyright notice and disclaimer of warranty; keep intact | |||
all the notices that refer to this License and to the absence of any | |||
warranty; and distribute a copy of this License along with the | |||
Library. | |||
You may charge a fee for the physical act of transferring a copy, | |||
and you may at your option offer warranty protection in exchange for a | |||
fee. | |||
2. You may modify your copy or copies of the Library or any portion | |||
of it, thus forming a work based on the Library, and copy and | |||
distribute such modifications or work under the terms of Section 1 | |||
above, provided that you also meet all of these conditions: | |||
a) The modified work must itself be a software library. | |||
b) You must cause the files modified to carry prominent notices | |||
stating that you changed the files and the date of any change. | |||
c) You must cause the whole of the work to be licensed at no | |||
charge to all third parties under the terms of this License. | |||
d) If a facility in the modified Library refers to a function or a | |||
table of data to be supplied by an application program that uses | |||
the facility, other than as an argument passed when the facility | |||
is invoked, then you must make a good faith effort to ensure that, | |||
in the event an application does not supply such function or | |||
table, the facility still operates, and performs whatever part of | |||
its purpose remains meaningful. | |||
(For example, a function in a library to compute square roots has | |||
a purpose that is entirely well-defined independent of the | |||
application. Therefore, Subsection 2d requires that any | |||
application-supplied function or table used by this function must | |||
be optional: if the application does not supply it, the square | |||
root function must still compute square roots.) | |||
These requirements apply to the modified work as a whole. If | |||
identifiable sections of that work are not derived from the Library, | |||
and can be reasonably considered independent and separate works in | |||
themselves, then this License, and its terms, do not apply to those | |||
sections when you distribute them as separate works. But when you | |||
distribute the same sections as part of a whole which is a work based | |||
on the Library, the distribution of the whole must be on the terms of | |||
this License, whose permissions for other licensees extend to the | |||
entire whole, and thus to each and every part regardless of who wrote | |||
it. | |||
Thus, it is not the intent of this section to claim rights or contest | |||
your rights to work written entirely by you; rather, the intent is to | |||
exercise the right to control the distribution of derivative or | |||
collective works based on the Library. | |||
In addition, mere aggregation of another work not based on the Library | |||
with the Library (or with a work based on the Library) on a volume of | |||
a storage or distribution medium does not bring the other work under | |||
the scope of this License. | |||
3. You may opt to apply the terms of the ordinary GNU General Public | |||
License instead of this License to a given copy of the Library. To do | |||
this, you must alter all the notices that refer to this License, so | |||
that they refer to the ordinary GNU General Public License, version 2, | |||
instead of to this License. (If a newer version than version 2 of the | |||
ordinary GNU General Public License has appeared, then you can specify | |||
that version instead if you wish.) Do not make any other change in | |||
these notices. | |||
Once this change is made in a given copy, it is irreversible for | |||
that copy, so the ordinary GNU General Public License applies to all | |||
subsequent copies and derivative works made from that copy. | |||
This option is useful when you wish to copy part of the code of | |||
the Library into a program that is not a library. | |||
4. You may copy and distribute the Library (or a portion or | |||
derivative of it, under Section 2) in object code or executable form | |||
under the terms of Sections 1 and 2 above provided that you accompany | |||
it with the complete corresponding machine-readable source code, which | |||
must be distributed under the terms of Sections 1 and 2 above on a | |||
medium customarily used for software interchange. | |||
If distribution of object code is made by offering access to copy | |||
from a designated place, then offering equivalent access to copy the | |||
source code from the same place satisfies the requirement to | |||
distribute the source code, even though third parties are not | |||
compelled to copy the source along with the object code. | |||
5. A program that contains no derivative of any portion of the | |||
Library, but is designed to work with the Library by being compiled or | |||
linked with it, is called a "work that uses the Library". Such a | |||
work, in isolation, is not a derivative work of the Library, and | |||
therefore falls outside the scope of this License. | |||
However, linking a "work that uses the Library" with the Library | |||
creates an executable that is a derivative of the Library (because it | |||
contains portions of the Library), rather than a "work that uses the | |||
library". The executable is therefore covered by this License. | |||
Section 6 states terms for distribution of such executables. | |||
When a "work that uses the Library" uses material from a header file | |||
that is part of the Library, the object code for the work may be a | |||
derivative work of the Library even though the source code is not. | |||
Whether this is true is especially significant if the work can be | |||
linked without the Library, or if the work is itself a library. The | |||
threshold for this to be true is not precisely defined by law. | |||
If such an object file uses only numerical parameters, data | |||
structure layouts and accessors, and small macros and small inline | |||
functions (ten lines or less in length), then the use of the object | |||
file is unrestricted, regardless of whether it is legally a derivative | |||
work. (Executables containing this object code plus portions of the | |||
Library will still fall under Section 6.) | |||
Otherwise, if the work is a derivative of the Library, you may | |||
distribute the object code for the work under the terms of Section 6. | |||
Any executables containing that work also fall under Section 6, | |||
whether or not they are linked directly with the Library itself. | |||
6. As an exception to the Sections above, you may also combine or | |||
link a "work that uses the Library" with the Library to produce a | |||
work containing portions of the Library, and distribute that work | |||
under terms of your choice, provided that the terms permit | |||
modification of the work for the customer's own use and reverse | |||
engineering for debugging such modifications. | |||
You must give prominent notice with each copy of the work that the | |||
Library is used in it and that the Library and its use are covered by | |||
this License. You must supply a copy of this License. If the work | |||
during execution displays copyright notices, you must include the | |||
copyright notice for the Library among them, as well as a reference | |||
directing the user to the copy of this License. Also, you must do one | |||
of these things: | |||
a) Accompany the work with the complete corresponding | |||
machine-readable source code for the Library including whatever | |||
changes were used in the work (which must be distributed under | |||
Sections 1 and 2 above); and, if the work is an executable linked | |||
with the Library, with the complete machine-readable "work that | |||
uses the Library", as object code and/or source code, so that the | |||
user can modify the Library and then relink to produce a modified | |||
executable containing the modified Library. (It is understood | |||
that the user who changes the contents of definitions files in the | |||
Library will not necessarily be able to recompile the application | |||
to use the modified definitions.) | |||
b) Use a suitable shared library mechanism for linking with the | |||
Library. A suitable mechanism is one that (1) uses at run time a | |||
copy of the library already present on the user's computer system, | |||
rather than copying library functions into the executable, and (2) | |||
will operate properly with a modified version of the library, if | |||
the user installs one, as long as the modified version is | |||
interface-compatible with the version that the work was made with. | |||
c) Accompany the work with a written offer, valid for at | |||
least three years, to give the same user the materials | |||
specified in Subsection 6a, above, for a charge no more | |||
than the cost of performing this distribution. | |||
d) If distribution of the work is made by offering access to copy | |||
from a designated place, offer equivalent access to copy the above | |||
specified materials from the same place. | |||
e) Verify that the user has already received a copy of these | |||
materials or that you have already sent this user a copy. | |||
For an executable, the required form of the "work that uses the | |||
Library" must include any data and utility programs needed for | |||
reproducing the executable from it. However, as a special exception, | |||
the materials to be distributed need not include anything that is | |||
normally distributed (in either source or binary form) with the major | |||
components (compiler, kernel, and so on) of the operating system on | |||
which the executable runs, unless that component itself accompanies | |||
the executable. | |||
It may happen that this requirement contradicts the license | |||
restrictions of other proprietary libraries that do not normally | |||
accompany the operating system. Such a contradiction means you cannot | |||
use both them and the Library together in an executable that you | |||
distribute. | |||
7. You may place library facilities that are a work based on the | |||
Library side-by-side in a single library together with other library | |||
facilities not covered by this License, and distribute such a combined | |||
library, provided that the separate distribution of the work based on | |||
the Library and of the other library facilities is otherwise | |||
permitted, and provided that you do these two things: | |||
a) Accompany the combined library with a copy of the same work | |||
based on the Library, uncombined with any other library | |||
facilities. This must be distributed under the terms of the | |||
Sections above. | |||
b) Give prominent notice with the combined library of the fact | |||
that part of it is a work based on the Library, and explaining | |||
where to find the accompanying uncombined form of the same work. | |||
8. You may not copy, modify, sublicense, link with, or distribute | |||
the Library except as expressly provided under this License. Any | |||
attempt otherwise to copy, modify, sublicense, link with, or | |||
distribute the Library is void, and will automatically terminate your | |||
rights under this License. However, parties who have received copies, | |||
or rights, from you under this License will not have their licenses | |||
terminated so long as such parties remain in full compliance. | |||
9. You are not required to accept this License, since you have not | |||
signed it. However, nothing else grants you permission to modify or | |||
distribute the Library or its derivative works. These actions are | |||
prohibited by law if you do not accept this License. Therefore, by | |||
modifying or distributing the Library (or any work based on the | |||
Library), you indicate your acceptance of this License to do so, and | |||
all its terms and conditions for copying, distributing or modifying | |||
the Library or works based on it. | |||
10. Each time you redistribute the Library (or any work based on the | |||
Library), the recipient automatically receives a license from the | |||
original licensor to copy, distribute, link with or modify the Library | |||
subject to these terms and conditions. You may not impose any further | |||
restrictions on the recipients' exercise of the rights granted herein. | |||
You are not responsible for enforcing compliance by third parties with | |||
this License. | |||
11. If, as a consequence of a court judgment or allegation of patent | |||
infringement or for any other reason (not limited to patent issues), | |||
conditions are imposed on you (whether by court order, agreement or | |||
otherwise) that contradict the conditions of this License, they do not | |||
excuse you from the conditions of this License. If you cannot | |||
distribute so as to satisfy simultaneously your obligations under this | |||
License and any other pertinent obligations, then as a consequence you | |||
may not distribute the Library at all. For example, if a patent | |||
license would not permit royalty-free redistribution of the Library by | |||
all those who receive copies directly or indirectly through you, then | |||
the only way you could satisfy both it and this License would be to | |||
refrain entirely from distribution of the Library. | |||
If any portion of this section is held invalid or unenforceable under any | |||
particular circumstance, the balance of the section is intended to apply, | |||
and the section as a whole is intended to apply in other circumstances. | |||
It is not the purpose of this section to induce you to infringe any | |||
patents or other property right claims or to contest validity of any | |||
such claims; this section has the sole purpose of protecting the | |||
integrity of the free software distribution system which is | |||
implemented by public license practices. Many people have made | |||
generous contributions to the wide range of software distributed | |||
through that system in reliance on consistent application of that | |||
system; it is up to the author/donor to decide if he or she is willing | |||
to distribute software through any other system and a licensee cannot | |||
impose that choice. | |||
This section is intended to make thoroughly clear what is believed to | |||
be a consequence of the rest of this License. | |||
12. If the distribution and/or use of the Library is restricted in | |||
certain countries either by patents or by copyrighted interfaces, the | |||
original copyright holder who places the Library under this License may add | |||
an explicit geographical distribution limitation excluding those countries, | |||
so that distribution is permitted only in or among countries not thus | |||
excluded. In such case, this License incorporates the limitation as if | |||
written in the body of this License. | |||
13. The Free Software Foundation may publish revised and/or new | |||
versions of the Lesser General Public License from time to time. | |||
Such new versions will be similar in spirit to the present version, | |||
but may differ in detail to address new problems or concerns. | |||
Each version is given a distinguishing version number. If the Library | |||
specifies a version number of this License which applies to it and | |||
"any later version", you have the option of following the terms and | |||
conditions either of that version or of any later version published by | |||
the Free Software Foundation. If the Library does not specify a | |||
license version number, you may choose any version ever published by | |||
the Free Software Foundation. | |||
14. If you wish to incorporate parts of the Library into other free | |||
programs whose distribution conditions are incompatible with these, | |||
write to the author to ask for permission. For software which is | |||
copyrighted by the Free Software Foundation, write to the Free | |||
Software Foundation; we sometimes make exceptions for this. Our | |||
decision will be guided by the two goals of preserving the free status | |||
of all derivatives of our free software and of promoting the sharing | |||
and reuse of software generally. | |||
NO WARRANTY | |||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | |||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | |||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | |||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | |||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | |||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | |||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | |||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | |||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | |||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | |||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | |||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | |||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | |||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | |||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | |||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |||
DAMAGES. | |||
END OF TERMS AND CONDITIONS | |||
How to Apply These Terms to Your New Libraries | |||
If you develop a new library, and you want it to be of the greatest | |||
possible use to the public, we recommend making it free software that | |||
everyone can redistribute and change. You can do so by permitting | |||
redistribution under these terms (or, alternatively, under the terms of the | |||
ordinary General Public License). | |||
To apply these terms, attach the following notices to the library. It is | |||
safest to attach them to the start of each source file to most effectively | |||
convey the exclusion of warranty; and each file should have at least the | |||
"copyright" line and a pointer to where the full notice is found. | |||
<one line to give the library's name and a brief idea of what it does.> | |||
Copyright (C) <year> <name of author> | |||
This library is free software; you can redistribute it and/or | |||
modify it under the terms of the GNU Lesser General Public | |||
License as published by the Free Software Foundation; either | |||
version 2.1 of the License, or (at your option) any later version. | |||
This library is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
Lesser General Public License for more details. | |||
You should have received a copy of the GNU Lesser General Public | |||
License along with this library; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
Also add information on how to contact you by electronic and paper mail. | |||
You should also get your employer (if you work as a programmer) or your | |||
school, if any, to sign a "copyright disclaimer" for the library, if | |||
necessary. Here is a sample; alter the names: | |||
Yoyodyne, Inc., hereby disclaims all copyright interest in the | |||
library `Frob' (a library for tweaking knobs) written by James Random Hacker. | |||
<signature of Ty Coon>, 1 April 1990 | |||
Ty Coon, President of Vice | |||
That's all there is to it! | |||
vim: et sw=4 sts=4 |
@@ -0,0 +1,3 @@ | |||
The contents of the library/ folder should be here. | |||
vim: et sw=4 sts=4 |
@@ -1,2 +0,0 @@ | |||
vendor | |||
composer.lock |
@@ -1 +0,0 @@ | |||
Subproject commit c9a888f5755d09bb10a9e5c0675e13565262a1bb |
@@ -0,0 +1,19 @@ | |||
Change Log: `yii2-mpdf` | |||
======================= | |||
## Version 1.0.1 | |||
**Date:** 08-Apr-2016 | |||
- (enh #12): New `tempPath` property to allow setting temporary folder for mpdf font data. | |||
- (enh #13): Default mode setting for Asian Languages via `Pdf::MODE_ASIAN`. | |||
- (enh #14): Initialize with default mPDF configuration options. | |||
- Update mpdf source to use repo https://github.com/mpdf/mpdf. | |||
- Add branch alias for dev-master latest release. | |||
## Version 1.0.0 | |||
**Date:** 03-Nov-2014 | |||
- Initial release. | |||
- Set release to stable. |
@@ -0,0 +1,28 @@ | |||
Copyright (c) 2014 - 2015, Kartik Visweswaran | |||
Krajee.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 names of Kartik Visweswaran or Krajee 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 HOLDER 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. |
@@ -0,0 +1,405 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright © Kartik Visweswaran, Krajee.com, 2014 - 2015 | |||
* @package yii2-mpdf | |||
* @version 1.0.1 | |||
*/ | |||
namespace kartik\mpdf; | |||
use Yii; | |||
use yii\base\Component; | |||
use yii\base\InvalidConfigException; | |||
use yii\base\InvalidParamException; | |||
use \mPDF; | |||
/** | |||
* PDF library component wrapping the mPDF class with additional enhancements. | |||
* | |||
* @author Kartik Visweswaran <kartikv2@gmail.com> | |||
* @since 1.0 | |||
*/ | |||
class Pdf extends Component | |||
{ | |||
// mode | |||
const MODE_BLANK = ''; | |||
const MODE_CORE = 'c'; | |||
const MODE_UTF8 = 'UTF-8'; | |||
const MODE_ASIAN = '+aCJK'; | |||
// format | |||
const FORMAT_A3 = 'A3'; | |||
const FORMAT_A4 = 'A4'; | |||
const FORMAT_LETTER = 'Letter'; | |||
const FORMAT_LEGAL = 'Legal'; | |||
const FORMAT_FOLIO = 'Folio'; | |||
const FORMAT_LEDGER = 'Ledger-L'; | |||
const FORMAT_TABLOID = 'Tabloid'; | |||
// orientation | |||
const ORIENT_PORTRAIT = 'P'; | |||
const ORIENT_LANDSCAPE = 'L'; | |||
// output destination | |||
const DEST_BROWSER = 'I'; | |||
const DEST_DOWNLOAD = 'D'; | |||
const DEST_FILE = 'F'; | |||
const DEST_STRING = 'S'; | |||
/** | |||
* @var string specifies the mode of the new document. If the mode is set by passing a country/language string, | |||
* this may also set: available fonts, text justification, and directionality RTL. | |||
*/ | |||
public $mode = self::MODE_BLANK; | |||
/** | |||
* @var string|array, the format can be specified either as a pre-defined page size, | |||
* or as an array of width and height in millimetres. | |||
*/ | |||
public $format = self::FORMAT_A4; | |||
/** | |||
* @var int sets the default document font size in points (pt) | |||
*/ | |||
public $defaultFontSize = 0; | |||
/** | |||
* @var string sets the default font-family for the new document. Uses default value set in defaultCSS | |||
* unless codepage has been set to "win-1252". If codepage="win-1252", the appropriate core Adobe font | |||
* will be set i.e. Helvetica, Times, or Courier. | |||
*/ | |||
public $defaultFont = ''; | |||
/** | |||
* @var float sets the page left margin for the new document. All values should be specified as LENGTH in millimetres. | |||
* If you are creating a DOUBLE-SIDED document, the margin values specified will be used for ODD pages; left and right margins | |||
* will be mirrored for EVEN pages. | |||
*/ | |||
public $marginLeft = 15; | |||
/** | |||
* @var float sets the page right margin for the new document (in millimetres). | |||
*/ | |||
public $marginRight = 15; | |||
/** | |||
* @var float sets the page top margin for the new document (in millimetres). | |||
*/ | |||
public $marginTop = 16; | |||
/** | |||
* @var float sets the page bottom margin for the new document (in millimetres). | |||
*/ | |||
public $marginBottom = 16; | |||
/** | |||
* @var float sets the page header margin for the new document (in millimetres). | |||
*/ | |||
public $marginHeader = 9; | |||
/** | |||
* @var float sets the page footer margin for the new document (in millimetres). | |||
*/ | |||
public $marginFooter = 9; | |||
/** | |||
* @var string specifies the default page orientation of the new document. | |||
*/ | |||
public $orientation = self::ORIENT_PORTRAIT; | |||
/** | |||
* @var string css file to prepend to the PDF | |||
*/ | |||
public $cssFile = '@vendor/kartik-v/yii2-mpdf/assets/kv-mpdf-bootstrap.min.css'; | |||
/** | |||
* @var string additional inline css to append after the cssFile | |||
*/ | |||
public $cssInline = ''; | |||
/** | |||
* @var string the HTML content to be converted to PDF | |||
*/ | |||
public $content = ''; | |||
/** | |||
* @var string the output filename | |||
*/ | |||
public $filename = ''; | |||
/** | |||
* @var string the output destination | |||
*/ | |||
public $destination = self::DEST_BROWSER; | |||
/** | |||
* @var string the folder path for storing the temporary data generated by mpdf. | |||
* If not set this defaults to `Yii::getAlias('@runtime/mpdf')`. | |||
*/ | |||
public $tempPath; | |||
/** | |||
* @var array the mPDF methods that will called in the sequence listed before | |||
* rendering the content. Should be an associative array of $method => $params | |||
* format, where: | |||
* - `$method`: string, is the mPDF method / function name | |||
* - `$param`: mixed, are the mPDF method parameters | |||
*/ | |||
public $methods = ''; | |||
/** | |||
* @var string the mPDF configuration options entered as a `$key => value` | |||
* associative array, where: | |||
* - `$key`: string is the mPDF configuration property name | |||
* - `$value`: mixed is the mPDF configured property value | |||
*/ | |||
public $options = [ | |||
'autoScriptToLang' => true, | |||
'ignore_invalid_utf8' => true, | |||
'tabSpaces' => 4 | |||
]; | |||
/** | |||
* @var mPDF api instance | |||
*/ | |||
protected $_mpdf; | |||
/** | |||
* @var string the css file content | |||
*/ | |||
protected $_css; | |||
/** | |||
* | |||
* @var array Array of file-pathes that should be attached to the generated PDF | |||
*/ | |||
protected $_pdfAttachements; | |||
/** | |||
* @inherit doc | |||
*/ | |||
public function init() | |||
{ | |||
$this->initTempPaths(); | |||
parent::init(); | |||
$this->parseFormat(); | |||
} | |||
/** | |||
* Initialize folder paths to allow mpdf to write temporary data. | |||
*/ | |||
public function initTempPaths() | |||
{ | |||
if (empty($this->tempPath)) { | |||
$this->tempPath = Yii::getAlias('@runtime/mpdf'); | |||
} | |||
$prefix = $this->tempPath . DIRECTORY_SEPARATOR; | |||
static::definePath('_MPDF_TEMP_PATH', "{$prefix}tmp"); | |||
static::definePath('_MPDF_TTFONTDATAPATH', "{$prefix}ttfontdata"); | |||
} | |||
/** | |||
* Defines a mPDF temporary path if not set | |||
* | |||
* @param string $prop the mPDF constant to define | |||
* @param string $dir the directory to create | |||
* | |||
* @return bool | |||
* @throws InvalidConfigException | |||
*/ | |||
protected static function definePath($prop, $dir) | |||
{ | |||
if (defined($prop)) { | |||
return; | |||
} | |||
$status = true; | |||
if (!is_dir($dir)) { | |||
$status = mkdir($dir, 0777, true); | |||
} | |||
if (!$status) { | |||
throw new InvalidConfigException("Could not create the folder '{$dir}' in '\$tempPath' set."); | |||
} | |||
define($prop, $dir); | |||
} | |||
/** | |||
* Renders and returns the PDF output. Uses the class level property settings. | |||
*/ | |||
public function render() | |||
{ | |||
$this->configure($this->options); | |||
if (!empty($this->methods)) { | |||
foreach ($this->methods as $method => $param) { | |||
$this->execute($method, $param); | |||
} | |||
} | |||
return $this->output($this->content, $this->filename, $this->destination); | |||
} | |||
/** | |||
* Initializes (if needed) and fetches the mPDF API instance | |||
* @return mPDF instance | |||
*/ | |||
public function getApi() | |||
{ | |||
if (empty($this->_mpdf) || !$this->_mpdf instanceof mPDF) { | |||
$this->setApi(); | |||
} | |||
return $this->_mpdf; | |||
} | |||
/** | |||
* Sets the mPDF API instance | |||
*/ | |||
public function setApi() | |||
{ | |||
$this->_mpdf = new mPDF( | |||
$this->mode, | |||
$this->format, | |||
$this->defaultFontSize, | |||
$this->defaultFont, | |||
$this->marginLeft, | |||
$this->marginRight, | |||
$this->marginTop, | |||
$this->marginBottom, | |||
$this->marginHeader, | |||
$this->marginFooter, | |||
$this->orientation | |||
); | |||
} | |||
/** | |||
* Fetches the content of the CSS file if supplied | |||
* @return string | |||
*/ | |||
public function getCss() | |||
{ | |||
if (!empty($this->_css)) { | |||
return $this->_css; | |||
} | |||
$cssFile = empty($this->cssFile) ? '' : Yii::getAlias($this->cssFile); | |||
if (empty($cssFile) || !file_exists($cssFile)) { | |||
$css = ''; | |||
} else { | |||
$css = file_get_contents($cssFile); | |||
} | |||
$css .= $this->cssInline; | |||
return $css; | |||
} | |||
/** | |||
* @return array Array of attachements | |||
*/ | |||
public function getPdfAttachements (){ | |||
return $this->_pdfAttachements; | |||
} | |||
/** | |||
* add an PDF to attach to the generated PDF | |||
* @param string $filePath | |||
*/ | |||
public function addPdfAttachement ($filePath){ | |||
$this->_pdfAttachements[] = $filePath; | |||
} | |||
/** | |||
* Configures mPDF options | |||
* @param array the mPDF configuration options entered as a `$key => value` | |||
* associative array, where: | |||
* - `$key`: string is the configuration property name | |||
* - `$value`: mixed is the configured property value | |||
*/ | |||
public function configure($options = []) | |||
{ | |||
if (empty($options)) { | |||
return; | |||
} | |||
$api = $this->api; | |||
foreach ($options as $key => $value) { | |||
if (property_exists($api, $key)) { | |||
$api->$key = $value; | |||
} | |||
} | |||
} | |||
/** | |||
* Calls the mPDF method with parameters | |||
* @param string method the mPDF method / function name | |||
* @param array params the mPDF parameters | |||
* @return mixed | |||
*/ | |||
public function execute($method, $params = []) | |||
{ | |||
$api = $this->api; | |||
if (!method_exists($api, $method)) { | |||
throw new InvalidParamException("Invalid or undefined mPDF method '{$method}' passed to 'Pdf::execute'."); | |||
} | |||
if (!is_array($params)) { | |||
$params = [$params]; | |||
} | |||
return call_user_func_array([$api, $method], $params); | |||
} | |||
/** | |||
* Generates a PDF output | |||
* @param string content, the input HTML content | |||
* @param string file, the name of the file. If not specified, the document will be | |||
* sent to the browser inline (destination I). | |||
* @param string dest, the destination. Defaults to Pdf::DEST_BROWSER. | |||
* @return mixed | |||
*/ | |||
public function output($content = '', $file = '', $dest = self::DEST_BROWSER) | |||
{ | |||
$api = $this->api; | |||
$css = $this->css; | |||
$pdfAttachements = $this->getPdfAttachements(); | |||
if (!empty($css)) { | |||
$api->WriteHTML($css, 1); | |||
$api->WriteHTML($content, 2); | |||
} else { | |||
$api->WriteHTML($content); | |||
} | |||
if($pdfAttachements){ | |||
$api->SetImportUse(); | |||
$api->SetHeader(null); | |||
$api->SetFooter(null); | |||
foreach ($pdfAttachements as $attachement){ | |||
$this->writePdfAttachement($api, $attachement); | |||
} | |||
} | |||
return $api->Output($file, $dest); | |||
} | |||
/** | |||
* appends the given attachement to the generated PDF | |||
* @param mPDF $api | |||
* @param String $attachement | |||
*/ | |||
private function writePdfAttachement ($api, $attachement){ | |||
try { | |||
$pageCount = $api->SetSourceFile($attachement); | |||
} catch (\MpdfException $e){ | |||
$pageCount = 0; | |||
} | |||
for($i=1; $i<=$pageCount; $i++){ | |||
$api->AddPage(); | |||
$templateId = $api->ImportPage($i); | |||
$api->UseTemplate($templateId); | |||
} | |||
} | |||
/** | |||
* Parse the format automatically based on the orientation | |||
*/ | |||
protected function parseFormat() | |||
{ | |||
$tag = '-' . self::ORIENT_LANDSCAPE; | |||
if ($this->orientation == self::ORIENT_LANDSCAPE && is_string($this->format) && substr($this->format, | |||
-2) != $tag | |||
) { | |||
$this->format .= $tag; | |||
} | |||
} | |||
} |
@@ -0,0 +1,124 @@ | |||
yii2-mpdf | |||
============= | |||
[![Stable Version](https://poser.pugx.org/kartik-v/yii2-mpdf/v/stable)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
[![Untable Version](https://poser.pugx.org/kartik-v/yii2-mpdf/v/unstable)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
[![License](https://poser.pugx.org/kartik-v/yii2-mpdf/license)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
[![Total Downloads](https://poser.pugx.org/kartik-v/yii2-mpdf/downloads)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
[![Monthly Downloads](https://poser.pugx.org/kartik-v/yii2-mpdf/d/monthly)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
[![Daily Downloads](https://poser.pugx.org/kartik-v/yii2-mpdf/d/daily)](https://packagist.org/packages/kartik-v/yii2-mpdf) | |||
The **yii2-mpdf** extension is a Yii2 wrapper component for the [mPDF library](http://www.mpdf1.com/) with enhancements. The mPDF library offers ability to generate PDF files from UTF-8 encoded HTML. This library is based on [FPDF](http://www.fpdf.org/) and [HTML2FPDF](http://html2fpdf.sourceforge.net/), with a number of enhancements. The key features in the library are to be able to generate PDF files 'on-the-fly' from HTML content, handling different languages. Refer the [documentation manual](https://mpdf.github.io) or the [upstream mpdf site](http://mpdf1.com) for further details and understanding of the library. The yii2-mpdf extension offers an easy way to integrate and use the mPDF library within your Yii application with subtle enhancements. The key features offerred with this release are: | |||
- Setup `pdf` component globally in your yii application configuration. | |||
- Setup mPDF properties or call mPDF methods easily using simple array configuration. | |||
- Enhances extension to setup your own custom CSS file for rendering the formatted HTML content. | |||
- Extension has a built-in version of bootstrap.css (v3.3.0 modified for mPDF) to be applied by default. This will allow you to generate PDF content from bootstrap markup HTML easily. | |||
- Offers easy way to prepend inline CSS in addition to your own CSS file. | |||
- Offers easy to use object oriented methods to render complex PDF. | |||
- Easy use of the extension like any Yii widget by using the render method with minimal configuration. | |||
- The extension uses the latest development version (v6.0beta) of the mPDF library. It uses the composer repository `kartik-v/mpdf` on packagist as a source for this latest version. mPDF 6.0 can utilise OpenType layout tables to display complex scripts. This release (v6.0) contains fonts (open source) to cover almost every imaginable script / language. Includes support for Arabic or Indic scripts (as well as Khmer, Lao, Myanmar etc.). It also is expected to improve the display of Thai, Vietnamese and Hebrew. It also includes additional fonts for Chinese, Japanese, and Korean. | |||
- Inbuilt integration with [yii2-grid](http://demos.krajee.com/grid) extension that allows you to export grid as PDF and even generate advanced PDF reports. | |||
### Demo | |||
Read the detailed [documentation and usage](http://demos.krajee.com/mpdf) of the extension. | |||
## Installation | |||
The preferred way to install this extension is through [composer](http://getcomposer.org/download/). | |||
> Note: Check the [composer.json](https://github.com/kartik-v/yii2-mpdf/blob/master/composer.json) for this extension's requirements and dependencies. | |||
Read this [web tip /wiki](http://webtips.krajee.com/setting-composer-minimum-stability-application/) on setting the `minimum-stability` settings for your application's composer.json. | |||
Either run | |||
``` | |||
$ php composer.phar require kartik-v/yii2-mpdf "dev-master" | |||
``` | |||
or add | |||
``` | |||
"kartik-v/yii2-mpdf": "dev-master" | |||
``` | |||
to the ```require``` section of your `composer.json` file. | |||
## Usage | |||
### Widget Like Usage | |||
The component can be used straightforward in a manner similar to any widget to render your HTML content as PDF. For example, you | |||
can call the component simply like below in your controller action: | |||
```php | |||
use kartik\mpdf\Pdf; | |||
public function actionReport() { | |||
// get your HTML raw content without any layouts or scripts | |||
$content = $this->renderPartial('_reportView'); | |||
// setup kartik\mpdf\Pdf component | |||
$pdf = new Pdf([ | |||
// set to use core fonts only | |||
'mode' => Pdf::MODE_CORE, | |||
// A4 paper format | |||
'format' => Pdf::FORMAT_A4, | |||
// portrait orientation | |||
'orientation' => Pdf::ORIENT_PORTRAIT, | |||
// stream to browser inline | |||
'destination' => Pdf::DEST_BROWSER, | |||
// your html content input | |||
'content' => $content, | |||
// format content from your own css file if needed or use the | |||
// enhanced bootstrap css built by Krajee for mPDF formatting | |||
'cssFile' => '@vendor/kartik-v/yii2-mpdf/assets/kv-mpdf-bootstrap.min.css', | |||
// any css to be embedded if required | |||
'cssInline' => '.kv-heading-1{font-size:18px}', | |||
// set mPDF properties on the fly | |||
'options' => ['title' => 'Krajee Report Title'], | |||
// call mPDF methods on the fly | |||
'methods' => [ | |||
'SetHeader'=>['Krajee Report Header'], | |||
'SetFooter'=>['{PAGENO}'], | |||
] | |||
]); | |||
// return the pdf output as per the destination setting | |||
return $pdf->render(); | |||
} | |||
``` | |||
### Global Component | |||
You can also setup the widget as a global component for use across your application with defaults preset. For example, setup the following in | |||
the components section of your Yii application configuration file: | |||
```php | |||
use kartik\mpdf\Pdf; | |||
// ... | |||
'components' => [ | |||
// setup Krajee Pdf component | |||
'pdf' => [ | |||
'class' => Pdf::classname(), | |||
'format' => Pdf::FORMAT_A4, | |||
'orientation' => Pdf::ORIENT_PORTRAIT, | |||
'destination' => Pdf::DEST_BROWSER, | |||
// refer settings section for all configuration options | |||
] | |||
] | |||
``` | |||
Once you have setup the component, you can refer it across your application easily: | |||
```php | |||
$pdf = Yii::$app->pdf; | |||
$pdf->content = $htmlContent; | |||
return $pdf->render(); | |||
``` | |||
For other usage and details, read the detailed [documentation](http://demos.krajee.com/mpdf). | |||
## License | |||
**yii2-mpdf** is released under the BSD 3-Clause License. See the bundled `LICENSE.md` for details. | |||
cd |
@@ -0,0 +1,28 @@ | |||
{ | |||
"name": "kartik-v/yii2-mpdf", | |||
"description": "A Yii2 wrapper component for the mPDF library which generates PDF files from UTF-8 encoded HTML.", | |||
"keywords": ["yii2", "extension", "pdf", "mpdf", "utf8", "html", "component"], | |||
"homepage": "https://github.com/kartik-v/yii2-mpdf", | |||
"type": "yii2-extension", | |||
"license": "BSD 3-Clause", | |||
"authors": [ | |||
{ | |||
"name": "Kartik Visweswaran", | |||
"email": "kartikv2@gmail.com", | |||
"homepage": "http://www.krajee.com/" | |||
} | |||
], | |||
"require": { | |||
"mpdf/mpdf": "<7.0" | |||
}, | |||
"autoload": { | |||
"psr-4": { | |||
"kartik\\mpdf\\": "" | |||
} | |||
}, | |||
"extra": { | |||
"branch-alias": { | |||
"dev-master": "1.0.x-dev" | |||
} | |||
} | |||
} |
@@ -1,2 +1 @@ | |||
vendor/* | |||
composer.lock | |||
@@ -0,0 +1,21 @@ | |||
The MIT License (MIT) | |||
Copyright (c) 2015 Setasign - Jan Slabon, https://www.setasign.com | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. |
@@ -0,0 +1,87 @@ | |||
FPDI - Free PDF Document Importer | |||
================================= | |||
[![Latest Stable Version](https://poser.pugx.org/setasign/fpdi/v/stable.svg)](https://packagist.org/packages/setasign/fpdi) [![Total Downloads](https://poser.pugx.org/setasign/fpdi/downloads.svg)](https://packagist.org/packages/setasign/fpdi) [![Latest Unstable Version](https://poser.pugx.org/setasign/fpdi/v/unstable.svg)](https://packagist.org/packages/setasign/fpdi) [![License](https://poser.pugx.org/setasign/fpdi/license.svg)](https://packagist.org/packages/setasign/fpdi) | |||
A clone of [FPDI](https://www.setasign.com/fpdi) for GitHub/[Composer](https://packagist.org/packages/setasign/fpdi). | |||
FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF, which was developed by Olivier Plathey. Apart from a copy of FPDF, FPDI does not require any special PHP extensions. | |||
## Installation with [Composer](https://packagist.org/packages/setasign/fpdi) | |||
FPDI is an add-on for [FPDF](http://fpdf.org/). Additionally FPDI can be used with [TCPDF](http://www.tcpdf.org/). | |||
For completion we added a [FPDF repository](https://github.com/Setasign/FPDF) which simply clones the offical releases. | |||
This package comes without any dependency configuration in the composer.json file. It's up to you to load the desired package as described below. | |||
A basic installation via Composer could be done this way: | |||
```bash | |||
$ composer require setasign/fpdi:1.6.1 | |||
``` | |||
or you can include the following in your composer.json file: | |||
```json | |||
{ | |||
"require": { | |||
"setasign/fpdi": "1.6.1" | |||
} | |||
} | |||
``` | |||
### Evaluate Dependencies Automatically | |||
To load dependencies automatically we prepared kind of metadata packages. To use FPDI with FPDF use [this](https://github.com/Setasign/FPDI-FPDF) package: | |||
```json | |||
{ | |||
"require": { | |||
"setasign/fpdi-fpdf": "1.6.1" | |||
} | |||
} | |||
``` | |||
For TCPDF use [this](https://github.com/Setasign/FPDI-TCPDF): | |||
```json | |||
{ | |||
"require": { | |||
"setasign/fpdi-tcpdf": "1.6.1" | |||
} | |||
} | |||
``` | |||
### Manual Dependencies | |||
To support both FPDF and TCPDF its up to you to load the preferred package before the classes of FPDI are loaded. By default FPDI will extend FPDF. If the TCPDF class exists, a new FPDF class will be created which will extend TCPDF while FPDI will extend this. | |||
To use FPDI with FPDF include following in your composer.json file: | |||
```json | |||
{ | |||
"require": { | |||
"setasign/fpdf": "1.8", | |||
"setasign/fpdi": "1.6.1" | |||
} | |||
} | |||
``` | |||
If you are using TCPDF, your have to update your composer.json respectively to: | |||
```json | |||
{ | |||
"require": { | |||
"tecnickcom/tcpdf": "6.2.12", | |||
"setasign/fpdi": "1.6.1" | |||
} | |||
} | |||
``` | |||
Additionally you have to trigger composers autoloader for the TCPDF class before you are initiating FPDI: | |||
```php | |||
class_exists('TCPDF', true); // trigger Composers autoloader to load the TCPDF class | |||
$pdf = new FPDI(); | |||
``` | |||
@@ -0,0 +1,30 @@ | |||
{ | |||
"name": "setasign/fpdi", | |||
"version": "1.6.1", | |||
"homepage": "https://www.setasign.com/fpdi", | |||
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", | |||
"type": "library", | |||
"keywords": ["pdf", "fpdi", "fpdf"], | |||
"license": "MIT", | |||
"authors": [ | |||
{ | |||
"name": "Jan Slabon", | |||
"email": "jan.slabon@setasign.com", | |||
"homepage": "https://www.setasign.com" | |||
} | |||
], | |||
"autoload": { | |||
"classmap": [ | |||
"filters/", | |||
"fpdi.php", | |||
"fpdf_tpl.php", | |||
"fpdi_pdf_parser.php", | |||
"pdf_context.php" | |||
] | |||
}, | |||
"suggest": { | |||
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use \"tecnickcom/tcpdf\" as an alternative there's no fixed dependency configured.", | |||
"setasign/fpdi-fpdf": "Use this package to automatically evaluate dependencies to FPDF.", | |||
"setasign/fpdi-tcpdf": "Use this package to automatically evaluate dependencies to TCPDF." | |||
} | |||
} |
@@ -0,0 +1,106 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* Class FilterASCII85 | |||
*/ | |||
class FilterASCII85 | |||
{ | |||
/** | |||
* Decode ASCII85 encoded string | |||
* | |||
* @param string $in | |||
* @return string | |||
* @throws Exception | |||
*/ | |||
public function decode($in) | |||
{ | |||
$ord = array( | |||
'~' => ord('~'), | |||
'z' => ord('z'), | |||
'u' => ord('u'), | |||
'!' => ord('!') | |||
); | |||
$out = ''; | |||
$state = 0; | |||
$chn = null; | |||
$l = strlen($in); | |||
for ($k = 0; $k < $l; ++$k) { | |||
$ch = ord($in[$k]) & 0xff; | |||
if ($ch == $ord['~']) { | |||
break; | |||
} | |||
if (preg_match('/^\s$/',chr($ch))) { | |||
continue; | |||
} | |||
if ($ch == $ord['z'] && $state == 0) { | |||
$out .= chr(0) . chr(0) . chr(0) . chr(0); | |||
continue; | |||
} | |||
if ($ch < $ord['!'] || $ch > $ord['u']) { | |||
throw new Exception('Illegal character in ASCII85Decode.'); | |||
} | |||
$chn[$state++] = $ch - $ord['!']; | |||
if ($state == 5) { | |||
$state = 0; | |||
$r = 0; | |||
for ($j = 0; $j < 5; ++$j) { | |||
$r = (int)($r * 85 + $chn[$j]); | |||
} | |||
$out .= chr($r >> 24); | |||
$out .= chr($r >> 16); | |||
$out .= chr($r >> 8); | |||
$out .= chr($r); | |||
} | |||
} | |||
$r = 0; | |||
if ($state == 1) { | |||
throw new Exception('Illegal length in ASCII85Decode.'); | |||
} | |||
if ($state == 2) { | |||
$r = $chn[0] * 85 * 85 * 85 * 85 + ($chn[1]+1) * 85 * 85 * 85; | |||
$out .= chr($r >> 24); | |||
} else if ($state == 3) { | |||
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + ($chn[2]+1) * 85 * 85; | |||
$out .= chr($r >> 24); | |||
$out .= chr($r >> 16); | |||
} else if ($state == 4) { | |||
$r = $chn[0] * 85 * 85 * 85 * 85 + $chn[1] * 85 * 85 * 85 + $chn[2] * 85 * 85 + ($chn[3]+1) * 85 ; | |||
$out .= chr($r >> 24); | |||
$out .= chr($r >> 16); | |||
$out .= chr($r >> 8); | |||
} | |||
return $out; | |||
} | |||
/** | |||
* NOT IMPLEMENTED | |||
* | |||
* @param string $in | |||
* @return string | |||
* @throws LogicException | |||
*/ | |||
public function encode($in) | |||
{ | |||
throw new LogicException("ASCII85 encoding not implemented."); | |||
} | |||
} |
@@ -0,0 +1,43 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* Class FilterASCIIHexDecode | |||
*/ | |||
class FilterASCIIHexDecode | |||
{ | |||
/** | |||
* Converts an ASCII hexadecimal encoded string into it's binary representation. | |||
* | |||
* @param string $data The input string | |||
* @return string | |||
*/ | |||
public function decode($data) | |||
{ | |||
$data = preg_replace('/[^0-9A-Fa-f]/', '', rtrim($data, '>')); | |||
if ((strlen($data) % 2) == 1) { | |||
$data .= '0'; | |||
} | |||
return pack('H*', $data); | |||
} | |||
/** | |||
* Converts a string into ASCII hexadecimal representation. | |||
* | |||
* @param string $data The input string | |||
* @param boolean $leaveEOD | |||
* @return string | |||
*/ | |||
public function encode($data, $leaveEOD = false) | |||
{ | |||
return current(unpack('H*', $data)) . ($leaveEOD ? '' : '>'); | |||
} | |||
} |
@@ -0,0 +1,164 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* Class FilterLZW | |||
*/ | |||
class FilterLZW | |||
{ | |||
protected $_sTable = array(); | |||
protected $_data = null; | |||
protected $_dataLength = 0; | |||
protected $_tIdx; | |||
protected $_bitsToGet = 9; | |||
protected $_bytePointer; | |||
protected $_bitPointer; | |||
protected $_nextData = 0; | |||
protected $_nextBits = 0; | |||
protected $_andTable = array(511, 1023, 2047, 4095); | |||
/** | |||
* Decodes LZW compressed data. | |||
* | |||
* @param string $data The compressed data. | |||
* @throws Exception | |||
* @return string | |||
*/ | |||
public function decode($data) | |||
{ | |||
if ($data[0] == 0x00 && $data[1] == 0x01) { | |||
throw new Exception('LZW flavour not supported.'); | |||
} | |||
$this->_initsTable(); | |||
$this->_data = $data; | |||
$this->_dataLength = strlen($data); | |||
// Initialize pointers | |||
$this->_bytePointer = 0; | |||
$this->_bitPointer = 0; | |||
$this->_nextData = 0; | |||
$this->_nextBits = 0; | |||
$oldCode = 0; | |||
$unCompData = ''; | |||
while (($code = $this->_getNextCode()) != 257) { | |||
if ($code == 256) { | |||
$this->_initsTable(); | |||
$code = $this->_getNextCode(); | |||
if ($code == 257) { | |||
break; | |||
} | |||
if (!isset($this->_sTable[$code])) { | |||
throw new Exception('Error while decompression LZW compressed data.'); | |||
} | |||
$unCompData .= $this->_sTable[$code]; | |||
$oldCode = $code; | |||
} else { | |||
if ($code < $this->_tIdx) { | |||
$string = $this->_sTable[$code]; | |||
$unCompData .= $string; | |||
$this->_addStringToTable($this->_sTable[$oldCode], $string[0]); | |||
$oldCode = $code; | |||
} else { | |||
$string = $this->_sTable[$oldCode]; | |||
$string = $string . $string[0]; | |||
$unCompData .= $string; | |||
$this->_addStringToTable($string); | |||
$oldCode = $code; | |||
} | |||
} | |||
} | |||
return $unCompData; | |||
} | |||
/** | |||
* Initialize the string table. | |||
*/ | |||
protected function _initsTable() | |||
{ | |||
$this->_sTable = array(); | |||
for ($i = 0; $i < 256; $i++) | |||
$this->_sTable[$i] = chr($i); | |||
$this->_tIdx = 258; | |||
$this->_bitsToGet = 9; | |||
} | |||
/** | |||
* Add a new string to the string table. | |||
*/ | |||
protected function _addStringToTable($oldString, $newString = '') | |||
{ | |||
$string = $oldString . $newString; | |||
// Add this new String to the table | |||
$this->_sTable[$this->_tIdx++] = $string; | |||
if ($this->_tIdx == 511) { | |||
$this->_bitsToGet = 10; | |||
} else if ($this->_tIdx == 1023) { | |||
$this->_bitsToGet = 11; | |||
} else if ($this->_tIdx == 2047) { | |||
$this->_bitsToGet = 12; | |||
} | |||
} | |||
/** | |||
* Returns the next 9, 10, 11 or 12 bits | |||
* | |||
* @return int | |||
*/ | |||
protected function _getNextCode() | |||
{ | |||
if ($this->_bytePointer == $this->_dataLength) { | |||
return 257; | |||
} | |||
$this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff); | |||
$this->_nextBits += 8; | |||
if ($this->_nextBits < $this->_bitsToGet) { | |||
$this->_nextData = ($this->_nextData << 8) | (ord($this->_data[$this->_bytePointer++]) & 0xff); | |||
$this->_nextBits += 8; | |||
} | |||
$code = ($this->_nextData >> ($this->_nextBits - $this->_bitsToGet)) & $this->_andTable[$this->_bitsToGet-9]; | |||
$this->_nextBits -= $this->_bitsToGet; | |||
return $code; | |||
} | |||
/** | |||
* NOT IMPLEMENTED | |||
* | |||
* @param string $in | |||
* @return string | |||
* @throws LogicException | |||
*/ | |||
public function encode($in) | |||
{ | |||
throw new LogicException("LZW encoding not implemented."); | |||
} | |||
} |
@@ -0,0 +1,548 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
if (!class_exists('fpdi_bridge')) { | |||
require_once('fpdi_bridge.php'); | |||
} | |||
/** | |||
* Class FPDF_TPL | |||
*/ | |||
class FPDF_TPL extends fpdi_bridge | |||
{ | |||
/** | |||
* Array of template data | |||
* | |||
* @var array | |||
*/ | |||
protected $_tpls = array(); | |||
/** | |||
* Current Template-Id | |||
* | |||
* @var int | |||
*/ | |||
public $tpl = 0; | |||
/** | |||
* "In Template"-Flag | |||
* | |||
* @var boolean | |||
*/ | |||
protected $_inTpl = false; | |||
/** | |||
* Name prefix of templates used in Resources dictionary | |||
* | |||
* @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an / | |||
*/ | |||
public $tplPrefix = "/TPL"; | |||
/** | |||
* Resources used by templates and pages | |||
* | |||
* @var array | |||
*/ | |||
protected $_res = array(); | |||
/** | |||
* Last used template data | |||
* | |||
* @var array | |||
*/ | |||
public $lastUsedTemplateData = array(); | |||
/** | |||
* Start a template. | |||
* | |||
* This method starts a template. You can give own coordinates to build an own sized | |||
* template. Pay attention, that the margins are adapted to the new template size. | |||
* If you want to write outside the template, for example to build a clipped template, | |||
* you have to set the margins and "cursor"-position manual after beginTemplate()-call. | |||
* | |||
* If no parameter is given, the template uses the current page-size. | |||
* The method returns an id of the current template. This id is used later for using this template. | |||
* Warning: A created template is saved in the resulting PDF at all events. Also if you don't use it after creation! | |||
* | |||
* @param int $x The x-coordinate given in user-unit | |||
* @param int $y The y-coordinate given in user-unit | |||
* @param int $w The width given in user-unit | |||
* @param int $h The height given in user-unit | |||
* @return int The id of new created template | |||
* @throws LogicException | |||
*/ | |||
public function beginTemplate($x = null, $y = null, $w = null, $h = null) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
throw new LogicException('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.'); | |||
} | |||
if ($this->page <= 0) { | |||
throw new LogicException("You have to add at least a page first!"); | |||
} | |||
if ($x == null) | |||
$x = 0; | |||
if ($y == null) | |||
$y = 0; | |||
if ($w == null) | |||
$w = $this->w; | |||
if ($h == null) | |||
$h = $this->h; | |||
// Save settings | |||
$this->tpl++; | |||
$tpl =& $this->_tpls[$this->tpl]; | |||
$tpl = array( | |||
'o_x' => $this->x, | |||
'o_y' => $this->y, | |||
'o_AutoPageBreak' => $this->AutoPageBreak, | |||
'o_bMargin' => $this->bMargin, | |||
'o_tMargin' => $this->tMargin, | |||
'o_lMargin' => $this->lMargin, | |||
'o_rMargin' => $this->rMargin, | |||
'o_h' => $this->h, | |||
'o_w' => $this->w, | |||
'o_FontFamily' => $this->FontFamily, | |||
'o_FontStyle' => $this->FontStyle, | |||
'o_FontSizePt' => $this->FontSizePt, | |||
'o_FontSize' => $this->FontSize, | |||
'buffer' => '', | |||
'x' => $x, | |||
'y' => $y, | |||
'w' => $w, | |||
'h' => $h | |||
); | |||
$this->SetAutoPageBreak(false); | |||
// Define own high and width to calculate correct positions | |||
$this->h = $h; | |||
$this->w = $w; | |||
$this->_inTpl = true; | |||
$this->SetXY($x + $this->lMargin, $y + $this->tMargin); | |||
$this->SetRightMargin($this->w - $w + $this->rMargin); | |||
if ($this->CurrentFont) { | |||
$fontKey = $this->FontFamily . $this->FontStyle; | |||
if ($fontKey) { | |||
$this->_res['tpl'][$this->tpl]['fonts'][$fontKey] =& $this->fonts[$fontKey]; | |||
$this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); | |||
} | |||
} | |||
return $this->tpl; | |||
} | |||
/** | |||
* End template. | |||
* | |||
* This method ends a template and reset initiated variables collected in {@link beginTemplate()}. | |||
* | |||
* @return int|boolean If a template is opened, the id is returned. If not a false is returned. | |||
*/ | |||
public function endTemplate() | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::endTemplate'), $args); | |||
} | |||
if ($this->_inTpl) { | |||
$this->_inTpl = false; | |||
$tpl = $this->_tpls[$this->tpl]; | |||
$this->SetXY($tpl['o_x'], $tpl['o_y']); | |||
$this->tMargin = $tpl['o_tMargin']; | |||
$this->lMargin = $tpl['o_lMargin']; | |||
$this->rMargin = $tpl['o_rMargin']; | |||
$this->h = $tpl['o_h']; | |||
$this->w = $tpl['o_w']; | |||
$this->SetAutoPageBreak($tpl['o_AutoPageBreak'], $tpl['o_bMargin']); | |||
$this->FontFamily = $tpl['o_FontFamily']; | |||
$this->FontStyle = $tpl['o_FontStyle']; | |||
$this->FontSizePt = $tpl['o_FontSizePt']; | |||
$this->FontSize = $tpl['o_FontSize']; | |||
$fontKey = $this->FontFamily . $this->FontStyle; | |||
if ($fontKey) | |||
$this->CurrentFont =& $this->fonts[$fontKey]; | |||
return $this->tpl; | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* Use a template in current page or other template. | |||
* | |||
* You can use a template in a page or in another template. | |||
* You can give the used template a new size. | |||
* All parameters are optional. The width or height is calculated automatically | |||
* if one is given. If no parameter is given the origin size as defined in | |||
* {@link beginTemplate()} method is used. | |||
* | |||
* The calculated or used width and height are returned as an array. | |||
* | |||
* @param int $tplIdx A valid template-id | |||
* @param int $x The x-position | |||
* @param int $y The y-position | |||
* @param int $w The new width of the template | |||
* @param int $h The new height of the template | |||
* @return array The height and width of the template (array('w' => ..., 'h' => ...)) | |||
* @throws LogicException|InvalidArgumentException | |||
*/ | |||
public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0) | |||
{ | |||
if ($this->page <= 0) { | |||
throw new LogicException('You have to add at least a page first!'); | |||
} | |||
if (!isset($this->_tpls[$tplIdx])) { | |||
throw new InvalidArgumentException('Template does not exist!'); | |||
} | |||
if ($this->_inTpl) { | |||
$this->_res['tpl'][$this->tpl]['tpls'][$tplIdx] =& $this->_tpls[$tplIdx]; | |||
} | |||
$tpl = $this->_tpls[$tplIdx]; | |||
$_w = $tpl['w']; | |||
$_h = $tpl['h']; | |||
if ($x == null) { | |||
$x = 0; | |||
} | |||
if ($y == null) { | |||
$y = 0; | |||
} | |||
$x += $tpl['x']; | |||
$y += $tpl['y']; | |||
$wh = $this->getTemplateSize($tplIdx, $w, $h); | |||
$w = $wh['w']; | |||
$h = $wh['h']; | |||
$tplData = array( | |||
'x' => $this->x, | |||
'y' => $this->y, | |||
'w' => $w, | |||
'h' => $h, | |||
'scaleX' => ($w / $_w), | |||
'scaleY' => ($h / $_h), | |||
'tx' => $x, | |||
'ty' => ($this->h - $y - $h), | |||
'lty' => ($this->h - $y - $h) - ($this->h - $_h) * ($h / $_h) | |||
); | |||
$this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm', | |||
$tplData['scaleX'], $tplData['scaleY'], $tplData['tx'] * $this->k, $tplData['ty'] * $this->k) | |||
); // Translate | |||
$this->_out(sprintf('%s%d Do Q', $this->tplPrefix, $tplIdx)); | |||
$this->lastUsedTemplateData = $tplData; | |||
return array('w' => $w, 'h' => $h); | |||
} | |||
/** | |||
* Get the calculated size of a template. | |||
* | |||
* If one size is given, this method calculates the other one. | |||
* | |||
* @param int $tplIdx A valid template-id | |||
* @param int $w The width of the template | |||
* @param int $h The height of the template | |||
* @return array The height and width of the template (array('w' => ..., 'h' => ...)) | |||
*/ | |||
public function getTemplateSize($tplIdx, $w = 0, $h = 0) | |||
{ | |||
if (!isset($this->_tpls[$tplIdx])) | |||
return false; | |||
$tpl = $this->_tpls[$tplIdx]; | |||
$_w = $tpl['w']; | |||
$_h = $tpl['h']; | |||
if ($w == 0 && $h == 0) { | |||
$w = $_w; | |||
$h = $_h; | |||
} | |||
if ($w == 0) | |||
$w = $h * $_w / $_h; | |||
if($h == 0) | |||
$h = $w * $_h / $_w; | |||
return array("w" => $w, "h" => $h); | |||
} | |||
/** | |||
* Sets the font used to print character strings. | |||
* | |||
* See FPDF/TCPDF documentation. | |||
* | |||
* @see http://fpdf.org/en/doc/setfont.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#afd56e360c43553830d543323e81bc045 | |||
*/ | |||
public function SetFont($family, $style = '', $size = null, $fontfile = '', $subset = 'default', $out = true) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::SetFont'), $args); | |||
} | |||
parent::SetFont($family, $style, $size); | |||
$fontkey = $this->FontFamily . $this->FontStyle; | |||
if ($this->_inTpl) { | |||
$this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey]; | |||
} else { | |||
$this->_res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey]; | |||
} | |||
} | |||
/** | |||
* Puts an image. | |||
* | |||
* See FPDF/TCPDF documentation. | |||
* | |||
* @see http://fpdf.org/en/doc/image.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#a714c2bee7d6b39d4d6d304540c761352 | |||
*/ | |||
public function Image( | |||
$file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false, | |||
$dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false, | |||
$hidden = false, $fitonpage = false, $alt = false, $altimgs = array() | |||
) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::Image'), $args); | |||
} | |||
$ret = parent::Image($file, $x, $y, $w, $h, $type, $link); | |||
if ($this->_inTpl) { | |||
$this->_res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file]; | |||
} else { | |||
$this->_res['page'][$this->page]['images'][$file] =& $this->images[$file]; | |||
} | |||
return $ret; | |||
} | |||
/** | |||
* Adds a new page to the document. | |||
* | |||
* See FPDF/TCPDF documentation. | |||
* | |||
* This method cannot be used if you'd started a template. | |||
* | |||
* @see http://fpdf.org/en/doc/addpage.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#a5171e20b366b74523709d84c349c1ced | |||
*/ | |||
public function AddPage($orientation = '', $format = '', $rotationOrKeepmargins = false, $tocpage = false) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::AddPage'), $args); | |||
} | |||
if ($this->_inTpl) { | |||
throw new LogicException('Adding pages in templates is not possible!'); | |||
} | |||
parent::AddPage($orientation, $format, $rotationOrKeepmargins); | |||
} | |||
/** | |||
* Puts a link on a rectangular area of the page. | |||
* | |||
* Overwritten because adding links in a template will not work. | |||
* | |||
* @see http://fpdf.org/en/doc/link.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#ab87bf1826384fbfe30eb499d42f1d994 | |||
*/ | |||
public function Link($x, $y, $w, $h, $link, $spaces = 0) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::Link'), $args); | |||
} | |||
if ($this->_inTpl) { | |||
throw new LogicException('Using links in templates is not posible!'); | |||
} | |||
parent::Link($x, $y, $w, $h, $link); | |||
} | |||
/** | |||
* Creates a new internal link and returns its identifier. | |||
* | |||
* Overwritten because adding links in a template will not work. | |||
* | |||
* @see http://fpdf.org/en/doc/addlink.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#a749522038ed7786c3e1701435dcb891e | |||
*/ | |||
public function AddLink() | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::AddLink'), $args); | |||
} | |||
if ($this->_inTpl) { | |||
throw new LogicException('Adding links in templates is not possible!'); | |||
} | |||
return parent::AddLink(); | |||
} | |||
/** | |||
* Defines the page and position a link points to. | |||
* | |||
* Overwritten because adding links in a template will not work. | |||
* | |||
* @see http://fpdf.org/en/doc/setlink.htm | |||
* @see http://www.tcpdf.org/doc/code/classTCPDF.html#ace5be60e7857953ea5e2b89cb90df0ae | |||
*/ | |||
public function SetLink($link, $y = 0, $page = -1) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$args = func_get_args(); | |||
return call_user_func_array(array($this, 'TCPDF::SetLink'), $args); | |||
} | |||
if ($this->_inTpl) { | |||
throw new LogicException('Setting links in templates is not possible!'); | |||
} | |||
parent::SetLink($link, $y, $page); | |||
} | |||
/** | |||
* Writes the form XObjects to the PDF document. | |||
*/ | |||
protected function _putformxobjects() | |||
{ | |||
$filter=($this->compress) ? '/Filter /FlateDecode ' : ''; | |||
reset($this->_tpls); | |||
foreach($this->_tpls AS $tplIdx => $tpl) { | |||
$this->_newobj(); | |||
$this->_tpls[$tplIdx]['n'] = $this->n; | |||
$this->_out('<<'.$filter.'/Type /XObject'); | |||
$this->_out('/Subtype /Form'); | |||
$this->_out('/FormType 1'); | |||
$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', | |||
// llx | |||
$tpl['x'] * $this->k, | |||
// lly | |||
-$tpl['y'] * $this->k, | |||
// urx | |||
($tpl['w'] + $tpl['x']) * $this->k, | |||
// ury | |||
($tpl['h'] - $tpl['y']) * $this->k | |||
)); | |||
if ($tpl['x'] != 0 || $tpl['y'] != 0) { | |||
$this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]', | |||
-$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2 | |||
)); | |||
} | |||
$this->_out('/Resources '); | |||
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); | |||
if (isset($this->_res['tpl'][$tplIdx])) { | |||
$res = $this->_res['tpl'][$tplIdx]; | |||
if (isset($res['fonts']) && count($res['fonts'])) { | |||
$this->_out('/Font <<'); | |||
foreach($res['fonts'] as $font) { | |||
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); | |||
} | |||
$this->_out('>>'); | |||
} | |||
if(isset($res['images']) || isset($res['tpls'])) { | |||
$this->_out('/XObject <<'); | |||
if (isset($res['images'])) { | |||
foreach($res['images'] as $image) | |||
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); | |||
} | |||
if (isset($res['tpls'])) { | |||
foreach($res['tpls'] as $i => $_tpl) | |||
$this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R'); | |||
} | |||
$this->_out('>>'); | |||
} | |||
} | |||
$this->_out('>>'); | |||
$buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; | |||
$this->_out('/Length ' . strlen($buffer) . ' >>'); | |||
$this->_putstream($buffer); | |||
$this->_out('endobj'); | |||
} | |||
} | |||
/** | |||
* Output images. | |||
* | |||
* Overwritten to add {@link _putformxobjects()} after _putimages(). | |||
*/ | |||
public function _putimages() | |||
{ | |||
parent::_putimages(); | |||
$this->_putformxobjects(); | |||
} | |||
/** | |||
* Writes the references of XObject resources to the document. | |||
* | |||
* Overwritten to add the the templates to the XObject resource dictionary. | |||
*/ | |||
public function _putxobjectdict() | |||
{ | |||
parent::_putxobjectdict(); | |||
foreach($this->_tpls as $tplIdx => $tpl) { | |||
$this->_out(sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n'])); | |||
} | |||
} | |||
/** | |||
* Writes bytes to the resulting document. | |||
* | |||
* Overwritten to delegate the data to the template buffer. | |||
* | |||
* @param string $s | |||
*/ | |||
public function _out($s) | |||
{ | |||
if ($this->state == 2 && $this->_inTpl) { | |||
$this->_tpls[$this->tpl]['buffer'] .= $s . "\n"; | |||
} else { | |||
parent::_out($s); | |||
} | |||
} | |||
} |
@@ -0,0 +1,693 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
if (!class_exists('FPDF_TPL')) { | |||
require_once('fpdf_tpl.php'); | |||
} | |||
/** | |||
* Class FPDI | |||
*/ | |||
class FPDI extends FPDF_TPL | |||
{ | |||
/** | |||
* FPDI version | |||
* | |||
* @string | |||
*/ | |||
const VERSION = '1.6.1'; | |||
/** | |||
* Actual filename | |||
* | |||
* @var string | |||
*/ | |||
public $currentFilename; | |||
/** | |||
* Parser-Objects | |||
* | |||
* @var fpdi_pdf_parser[] | |||
*/ | |||
public $parsers = array(); | |||
/** | |||
* Current parser | |||
* | |||
* @var fpdi_pdf_parser | |||
*/ | |||
public $currentParser; | |||
/** | |||
* The name of the last imported page box | |||
* | |||
* @var string | |||
*/ | |||
public $lastUsedPageBox; | |||
/** | |||
* Object stack | |||
* | |||
* @var array | |||
*/ | |||
protected $_objStack; | |||
/** | |||
* Done object stack | |||
* | |||
* @var array | |||
*/ | |||
protected $_doneObjStack; | |||
/** | |||
* Current Object Id. | |||
* | |||
* @var integer | |||
*/ | |||
protected $_currentObjId; | |||
/** | |||
* Cache for imported pages/template ids | |||
* | |||
* @var array | |||
*/ | |||
protected $_importedPages = array(); | |||
/** | |||
* Set a source-file. | |||
* | |||
* Depending on the PDF version of the used document the PDF version of the resulting document will | |||
* be adjusted to the higher version. | |||
* | |||
* @param string $filename A valid path to the PDF document from which pages should be imported from | |||
* @return int The number of pages in the document | |||
*/ | |||
public function setSourceFile($filename) | |||
{ | |||
$_filename = realpath($filename); | |||
if (false !== $_filename) | |||
$filename = $_filename; | |||
$this->currentFilename = $filename; | |||
if (!isset($this->parsers[$filename])) { | |||
$this->parsers[$filename] = $this->_getPdfParser($filename); | |||
$this->setPdfVersion( | |||
max($this->getPdfVersion(), $this->parsers[$filename]->getPdfVersion()) | |||
); | |||
} | |||
$this->currentParser = $this->parsers[$filename]; | |||
return $this->parsers[$filename]->getPageCount(); | |||
} | |||
/** | |||
* Returns a PDF parser object | |||
* | |||
* @param string $filename | |||
* @return fpdi_pdf_parser | |||
*/ | |||
protected function _getPdfParser($filename) | |||
{ | |||
if (!class_exists('fpdi_pdf_parser')) { | |||
require_once('fpdi_pdf_parser.php'); | |||
} | |||
return new fpdi_pdf_parser($filename); | |||
} | |||
/** | |||
* Get the current PDF version. | |||
* | |||
* @return string | |||
*/ | |||
public function getPdfVersion() | |||
{ | |||
return $this->PDFVersion; | |||
} | |||
/** | |||
* Set the PDF version. | |||
* | |||
* @param string $version | |||
*/ | |||
public function setPdfVersion($version = '1.3') | |||
{ | |||
$this->PDFVersion = sprintf('%.1F', $version); | |||
} | |||
/** | |||
* Import a page. | |||
* | |||
* The second parameter defines the bounding box that should be used to transform the page into a | |||
* form XObject. | |||
* | |||
* Following values are available: MediaBox, CropBox, BleedBox, TrimBox, ArtBox. | |||
* If a box is not especially defined its default box will be used: | |||
* | |||
* <ul> | |||
* <li>CropBox: Default -> MediaBox</li> | |||
* <li>BleedBox: Default -> CropBox</li> | |||
* <li>TrimBox: Default -> CropBox</li> | |||
* <li>ArtBox: Default -> CropBox</li> | |||
* </ul> | |||
* | |||
* It is possible to get the used page box by the {@link getLastUsedPageBox()} method. | |||
* | |||
* @param int $pageNo The page number | |||
* @param string $boxName The boundary box to use when transforming the page into a form XObject | |||
* @param boolean $groupXObject Define the form XObject as a group XObject to support transparency (if used) | |||
* @return int An id of the imported page/template to use with e.g. fpdf_tpl::useTemplate() | |||
* @throws LogicException|InvalidArgumentException | |||
* @see getLastUsedPageBox() | |||
*/ | |||
public function importPage($pageNo, $boxName = 'CropBox', $groupXObject = true) | |||
{ | |||
if ($this->_inTpl) { | |||
throw new LogicException('Please import the desired pages before creating a new template.'); | |||
} | |||
$fn = $this->currentFilename; | |||
$boxName = '/' . ltrim($boxName, '/'); | |||
// check if page already imported | |||
$pageKey = $fn . '-' . ((int)$pageNo) . $boxName; | |||
if (isset($this->_importedPages[$pageKey])) { | |||
return $this->_importedPages[$pageKey]; | |||
} | |||
$parser = $this->parsers[$fn]; | |||
$parser->setPageNo($pageNo); | |||
if (!in_array($boxName, $parser->availableBoxes)) { | |||
throw new InvalidArgumentException(sprintf('Unknown box: %s', $boxName)); | |||
} | |||
$pageBoxes = $parser->getPageBoxes($pageNo, $this->k); | |||
/** | |||
* MediaBox | |||
* CropBox: Default -> MediaBox | |||
* BleedBox: Default -> CropBox | |||
* TrimBox: Default -> CropBox | |||
* ArtBox: Default -> CropBox | |||
*/ | |||
if (!isset($pageBoxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox')) | |||
$boxName = '/CropBox'; | |||
if (!isset($pageBoxes[$boxName]) && $boxName == '/CropBox') | |||
$boxName = '/MediaBox'; | |||
if (!isset($pageBoxes[$boxName])) | |||
return false; | |||
$this->lastUsedPageBox = $boxName; | |||
$box = $pageBoxes[$boxName]; | |||
$this->tpl++; | |||
$this->_tpls[$this->tpl] = array(); | |||
$tpl =& $this->_tpls[$this->tpl]; | |||
$tpl['parser'] = $parser; | |||
$tpl['resources'] = $parser->getPageResources(); | |||
$tpl['buffer'] = $parser->getContent(); | |||
$tpl['box'] = $box; | |||
$tpl['groupXObject'] = $groupXObject; | |||
if ($groupXObject) { | |||
$this->setPdfVersion(max($this->getPdfVersion(), 1.4)); | |||
} | |||
// To build an array that can be used by PDF_TPL::useTemplate() | |||
$this->_tpls[$this->tpl] = array_merge($this->_tpls[$this->tpl], $box); | |||
// An imported page will start at 0,0 all the time. Translation will be set in _putformxobjects() | |||
$tpl['x'] = 0; | |||
$tpl['y'] = 0; | |||
// handle rotated pages | |||
$rotation = $parser->getPageRotation($pageNo); | |||
$tpl['_rotationAngle'] = 0; | |||
if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) { | |||
$steps = $angle / 90; | |||
$_w = $tpl['w']; | |||
$_h = $tpl['h']; | |||
$tpl['w'] = $steps % 2 == 0 ? $_w : $_h; | |||
$tpl['h'] = $steps % 2 == 0 ? $_h : $_w; | |||
if ($angle < 0) | |||
$angle += 360; | |||
$tpl['_rotationAngle'] = $angle * -1; | |||
} | |||
$this->_importedPages[$pageKey] = $this->tpl; | |||
return $this->tpl; | |||
} | |||
/** | |||
* Returns the last used page boundary box. | |||
* | |||
* @return string The used boundary box: MediaBox, CropBox, BleedBox, TrimBox or ArtBox | |||
*/ | |||
public function getLastUsedPageBox() | |||
{ | |||
return $this->lastUsedPageBox; | |||
} | |||
/** | |||
* Use a template or imported page in current page or other template. | |||
* | |||
* You can use a template in a page or in another template. | |||
* You can give the used template a new size. All parameters are optional. | |||
* The width or height is calculated automatically if one is given. If no | |||
* parameter is given the origin size as defined in beginTemplate() or of | |||
* the imported page is used. | |||
* | |||
* The calculated or used width and height are returned as an array. | |||
* | |||
* @param int $tplIdx A valid template-id | |||
* @param int $x The x-position | |||
* @param int $y The y-position | |||
* @param int $w The new width of the template | |||
* @param int $h The new height of the template | |||
* @param boolean $adjustPageSize If set to true the current page will be resized to fit the dimensions | |||
* of the template | |||
* | |||
* @return array The height and width of the template (array('w' => ..., 'h' => ...)) | |||
* @throws LogicException|InvalidArgumentException | |||
*/ | |||
public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0, $adjustPageSize = false) | |||
{ | |||
if ($adjustPageSize == true && is_null($x) && is_null($y)) { | |||
$size = $this->getTemplateSize($tplIdx, $w, $h); | |||
$orientation = $size['w'] > $size['h'] ? 'L' : 'P'; | |||
$size = array($size['w'], $size['h']); | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$this->setPageFormat($size, $orientation); | |||
} else { | |||
$size = $this->_getpagesize($size); | |||
if($orientation != $this->CurOrientation || | |||
$size[0] != $this->CurPageSize[0] || | |||
$size[1] != $this->CurPageSize[1] | |||
) { | |||
// New size or orientation | |||
if ($orientation=='P') { | |||
$this->w = $size[0]; | |||
$this->h = $size[1]; | |||
} else { | |||
$this->w = $size[1]; | |||
$this->h = $size[0]; | |||
} | |||
$this->wPt = $this->w * $this->k; | |||
$this->hPt = $this->h * $this->k; | |||
$this->PageBreakTrigger = $this->h - $this->bMargin; | |||
$this->CurOrientation = $orientation; | |||
$this->CurPageSize = $size; | |||
if (FPDF_VERSION >= 1.8) { | |||
$this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); | |||
} else { | |||
$this->PageSizes[$this->page] = array($this->wPt, $this->hPt); | |||
} | |||
} | |||
} | |||
} | |||
$this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values | |||
$size = parent::useTemplate($tplIdx, $x, $y, $w, $h); | |||
$this->_out('Q'); | |||
return $size; | |||
} | |||
/** | |||
* Copy all imported objects to the resulting document. | |||
*/ | |||
protected function _putimportedobjects() | |||
{ | |||
foreach($this->parsers AS $filename => $p) { | |||
$this->currentParser = $p; | |||
if (!isset($this->_objStack[$filename]) || !is_array($this->_objStack[$filename])) { | |||
continue; | |||
} | |||
while(($n = key($this->_objStack[$filename])) !== null) { | |||
try { | |||
$nObj = $this->currentParser->resolveObject($this->_objStack[$filename][$n][1]); | |||
} catch (Exception $e) { | |||
$nObj = array(pdf_parser::TYPE_OBJECT, pdf_parser::TYPE_NULL); | |||
} | |||
$this->_newobj($this->_objStack[$filename][$n][0]); | |||
if ($nObj[0] == pdf_parser::TYPE_STREAM) { | |||
$this->_writeValue($nObj); | |||
} else { | |||
$this->_writeValue($nObj[1]); | |||
} | |||
$this->_out("\nendobj"); | |||
$this->_objStack[$filename][$n] = null; // free memory | |||
unset($this->_objStack[$filename][$n]); | |||
reset($this->_objStack[$filename]); | |||
} | |||
} | |||
} | |||
/** | |||
* Writes the form XObjects to the PDF document. | |||
*/ | |||
protected function _putformxobjects() | |||
{ | |||
$filter = ($this->compress) ? '/Filter /FlateDecode ' : ''; | |||
reset($this->_tpls); | |||
foreach($this->_tpls AS $tplIdx => $tpl) { | |||
$this->_newobj(); | |||
$currentN = $this->n; // TCPDF/Protection: rem current "n" | |||
$this->_tpls[$tplIdx]['n'] = $this->n; | |||
$this->_out('<<' . $filter . '/Type /XObject'); | |||
$this->_out('/Subtype /Form'); | |||
$this->_out('/FormType 1'); | |||
$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', | |||
(isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k, | |||
(isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k, | |||
(isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k, | |||
(isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k | |||
)); | |||
$c = 1; | |||
$s = 0; | |||
$tx = 0; | |||
$ty = 0; | |||
if (isset($tpl['box'])) { | |||
$tx = -$tpl['box']['llx']; | |||
$ty = -$tpl['box']['lly']; | |||
if ($tpl['_rotationAngle'] <> 0) { | |||
$angle = $tpl['_rotationAngle'] * M_PI/180; | |||
$c = cos($angle); | |||
$s = sin($angle); | |||
switch($tpl['_rotationAngle']) { | |||
case -90: | |||
$tx = -$tpl['box']['lly']; | |||
$ty = $tpl['box']['urx']; | |||
break; | |||
case -180: | |||
$tx = $tpl['box']['urx']; | |||
$ty = $tpl['box']['ury']; | |||
break; | |||
case -270: | |||
$tx = $tpl['box']['ury']; | |||
$ty = -$tpl['box']['llx']; | |||
break; | |||
} | |||
} | |||
} else if ($tpl['x'] != 0 || $tpl['y'] != 0) { | |||
$tx = -$tpl['x'] * 2; | |||
$ty = $tpl['y'] * 2; | |||
} | |||
$tx *= $this->k; | |||
$ty *= $this->k; | |||
if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) { | |||
$this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]', | |||
$c, $s, -$s, $c, $tx, $ty | |||
)); | |||
} | |||
$this->_out('/Resources '); | |||
if (isset($tpl['resources'])) { | |||
$this->currentParser = $tpl['parser']; | |||
$this->_writeValue($tpl['resources']); // "n" will be changed | |||
} else { | |||
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); | |||
if (isset($this->_res['tpl'][$tplIdx])) { | |||
$res = $this->_res['tpl'][$tplIdx]; | |||
if (isset($res['fonts']) && count($res['fonts'])) { | |||
$this->_out('/Font <<'); | |||
foreach ($res['fonts'] as $font) | |||
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); | |||
$this->_out('>>'); | |||
} | |||
if (isset($res['images']) && count($res['images']) || | |||
isset($res['tpls']) && count($res['tpls'])) | |||
{ | |||
$this->_out('/XObject <<'); | |||
if (isset($res['images'])) { | |||
foreach ($res['images'] as $image) | |||
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); | |||
} | |||
if (isset($res['tpls'])) { | |||
foreach ($res['tpls'] as $i => $_tpl) | |||
$this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R'); | |||
} | |||
$this->_out('>>'); | |||
} | |||
$this->_out('>>'); | |||
} | |||
} | |||
if (isset($tpl['groupXObject']) && $tpl['groupXObject']) { | |||
$this->_out('/Group <</Type/Group/S/Transparency>>'); | |||
} | |||
$newN = $this->n; // TCPDF: rem new "n" | |||
$this->n = $currentN; // TCPDF: reset to current "n" | |||
$buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
$buffer = $this->_getrawstream($buffer); | |||
$this->_out('/Length ' . strlen($buffer) . ' >>'); | |||
$this->_out("stream\n" . $buffer . "\nendstream"); | |||
} else { | |||
$this->_out('/Length ' . strlen($buffer) . ' >>'); | |||
$this->_putstream($buffer); | |||
} | |||
$this->_out('endobj'); | |||
$this->n = $newN; // TCPDF: reset to new "n" | |||
} | |||
$this->_putimportedobjects(); | |||
} | |||
/** | |||
* Creates and optionally write the object definition to the document. | |||
* | |||
* Rewritten to handle existing own defined objects | |||
* | |||
* @param bool $objId | |||
* @param bool $onlyNewObj | |||
* @return bool|int | |||
*/ | |||
public function _newobj($objId = false, $onlyNewObj = false) | |||
{ | |||
if (!$objId) { | |||
$objId = ++$this->n; | |||
} | |||
// Begin a new object | |||
if (!$onlyNewObj) { | |||
$this->offsets[$objId] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer); | |||
$this->_out($objId . ' 0 obj'); | |||
$this->_currentObjId = $objId; // for later use with encryption | |||
} | |||
return $objId; | |||
} | |||
/** | |||
* Writes a PDF value to the resulting document. | |||
* | |||
* Needed to rebuild the source document | |||
* | |||
* @param mixed $value A PDF-Value. Structure of values see cases in this method | |||
*/ | |||
protected function _writeValue(&$value) | |||
{ | |||
if (is_subclass_of($this, 'TCPDF')) { | |||
parent::_prepareValue($value); | |||
} | |||
switch ($value[0]) { | |||
case pdf_parser::TYPE_TOKEN: | |||
$this->_straightOut($value[1] . ' '); | |||
break; | |||
case pdf_parser::TYPE_NUMERIC: | |||
case pdf_parser::TYPE_REAL: | |||
if (is_float($value[1]) && $value[1] != 0) { | |||
$this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' '); | |||
} else { | |||
$this->_straightOut($value[1] . ' '); | |||
} | |||
break; | |||
case pdf_parser::TYPE_ARRAY: | |||
// An array. Output the proper | |||
// structure and move on. | |||
$this->_straightOut('['); | |||
for ($i = 0; $i < count($value[1]); $i++) { | |||
$this->_writeValue($value[1][$i]); | |||
} | |||
$this->_out(']'); | |||
break; | |||
case pdf_parser::TYPE_DICTIONARY: | |||
// A dictionary. | |||
$this->_straightOut('<<'); | |||
reset ($value[1]); | |||
while (list($k, $v) = each($value[1])) { | |||
$this->_straightOut($k . ' '); | |||
$this->_writeValue($v); | |||
} | |||
$this->_straightOut('>>'); | |||
break; | |||
case pdf_parser::TYPE_OBJREF: | |||
// An indirect object reference | |||
// Fill the object stack if needed | |||
$cpfn =& $this->currentParser->filename; | |||
if (!isset($this->_doneObjStack[$cpfn][$value[1]])) { | |||
$this->_newobj(false, true); | |||
$this->_objStack[$cpfn][$value[1]] = array($this->n, $value); | |||
$this->_doneObjStack[$cpfn][$value[1]] = array($this->n, $value); | |||
} | |||
$objId = $this->_doneObjStack[$cpfn][$value[1]][0]; | |||
$this->_out($objId . ' 0 R'); | |||
break; | |||
case pdf_parser::TYPE_STRING: | |||
// A string. | |||
$this->_straightOut('(' . $value[1] . ')'); | |||
break; | |||
case pdf_parser::TYPE_STREAM: | |||
// A stream. First, output the | |||
// stream dictionary, then the | |||
// stream data itself. | |||
$this->_writeValue($value[1]); | |||
$this->_out('stream'); | |||
$this->_out($value[2][1]); | |||
$this->_straightOut("endstream"); | |||
break; | |||
case pdf_parser::TYPE_HEX: | |||
$this->_straightOut('<' . $value[1] . '>'); | |||
break; | |||
case pdf_parser::TYPE_BOOLEAN: | |||
$this->_straightOut($value[1] ? 'true ' : 'false '); | |||
break; | |||
case pdf_parser::TYPE_NULL: | |||
// The null object. | |||
$this->_straightOut('null '); | |||
break; | |||
} | |||
} | |||
/** | |||
* Modified _out() method so not each call will add a newline to the output. | |||
*/ | |||
protected function _straightOut($s) | |||
{ | |||
if (!is_subclass_of($this, 'TCPDF')) { | |||
if ($this->state == 2) { | |||
$this->pages[$this->page] .= $s; | |||
} else { | |||
$this->buffer .= $s; | |||
} | |||
} else { | |||
if ($this->state == 2) { | |||
if ($this->inxobj) { | |||
// we are inside an XObject template | |||
$this->xobjects[$this->xobjid]['outdata'] .= $s; | |||
} else if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { | |||
// puts data before page footer | |||
$pagebuff = $this->getPageBuffer($this->page); | |||
$page = substr($pagebuff, 0, -$this->footerlen[$this->page]); | |||
$footer = substr($pagebuff, -$this->footerlen[$this->page]); | |||
$this->setPageBuffer($this->page, $page . $s . $footer); | |||
// update footer position | |||
$this->footerpos[$this->page] += strlen($s); | |||
} else { | |||
// set page data | |||
$this->setPageBuffer($this->page, $s, true); | |||
} | |||
} else if ($this->state > 0) { | |||
// set general data | |||
$this->setBuffer($s); | |||
} | |||
} | |||
} | |||
/** | |||
* Ends the document | |||
* | |||
* Overwritten to close opened parsers | |||
*/ | |||
public function _enddoc() | |||
{ | |||
parent::_enddoc(); | |||
$this->_closeParsers(); | |||
} | |||
/** | |||
* Close all files opened by parsers. | |||
* | |||
* @return boolean | |||
*/ | |||
protected function _closeParsers() | |||
{ | |||
if ($this->state > 2) { | |||
$this->cleanUp(); | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Removes cycled references and closes the file handles of the parser objects. | |||
*/ | |||
public function cleanUp() | |||
{ | |||
while (($parser = array_pop($this->parsers)) !== null) { | |||
/** | |||
* @var fpdi_pdf_parser $parser | |||
*/ | |||
$parser->closeFile(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,206 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* This file is used as a bridge between TCPDF or FPDF | |||
* It will dynamically create the class extending the available | |||
* class FPDF or TCPDF. | |||
* | |||
* This way it is possible to use FPDI for both FPDF and TCPDF with one FPDI version. | |||
*/ | |||
if (!class_exists('TCPDF', false)) { | |||
/** | |||
* Class fpdi_bridge | |||
*/ | |||
class fpdi_bridge extends FPDF | |||
{ | |||
// empty body | |||
} | |||
} else { | |||
/** | |||
* Class fpdi_bridge | |||
*/ | |||
class fpdi_bridge extends TCPDF | |||
{ | |||
/** | |||
* Array of Tpl-Data | |||
* | |||
* @var array | |||
*/ | |||
protected $_tpls = array(); | |||
/** | |||
* Name-prefix of Templates used in Resources-Dictionary | |||
* | |||
* @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an / | |||
*/ | |||
public $tplPrefix = "/TPL"; | |||
/** | |||
* Current Object Id. | |||
* | |||
* @var integer | |||
*/ | |||
protected $_currentObjId; | |||
/** | |||
* Return XObjects Dictionary. | |||
* | |||
* Overwritten to add additional XObjects to the resources dictionary of TCPDF | |||
* | |||
* @return string | |||
*/ | |||
protected function _getxobjectdict() | |||
{ | |||
$out = parent::_getxobjectdict(); | |||
foreach ($this->_tpls as $tplIdx => $tpl) { | |||
$out .= sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']); | |||
} | |||
return $out; | |||
} | |||
/** | |||
* Writes a PDF value to the resulting document. | |||
* | |||
* Prepares the value for encryption of imported data by FPDI | |||
* | |||
* @param array $value | |||
*/ | |||
protected function _prepareValue(&$value) | |||
{ | |||
switch ($value[0]) { | |||
case pdf_parser::TYPE_STRING: | |||
if ($this->encrypted) { | |||
$value[1] = $this->_unescape($value[1]); | |||
$value[1] = $this->_encrypt_data($this->_currentObjId, $value[1]); | |||
$value[1] = TCPDF_STATIC::_escape($value[1]); | |||
} | |||
break; | |||
case pdf_parser::TYPE_STREAM: | |||
if ($this->encrypted) { | |||
$value[2][1] = $this->_encrypt_data($this->_currentObjId, $value[2][1]); | |||
$value[1][1]['/Length'] = array( | |||
pdf_parser::TYPE_NUMERIC, | |||
strlen($value[2][1]) | |||
); | |||
} | |||
break; | |||
case pdf_parser::TYPE_HEX: | |||
if ($this->encrypted) { | |||
$value[1] = $this->hex2str($value[1]); | |||
$value[1] = $this->_encrypt_data($this->_currentObjId, $value[1]); | |||
// remake hexstring of encrypted string | |||
$value[1] = $this->str2hex($value[1]); | |||
} | |||
break; | |||
} | |||
} | |||
/** | |||
* Un-escapes a PDF string | |||
* | |||
* @param string $s | |||
* @return string | |||
*/ | |||
protected function _unescape($s) | |||
{ | |||
$out = ''; | |||
for ($count = 0, $n = strlen($s); $count < $n; $count++) { | |||
if ($s[$count] != '\\' || $count == $n-1) { | |||
$out .= $s[$count]; | |||
} else { | |||
switch ($s[++$count]) { | |||
case ')': | |||
case '(': | |||
case '\\': | |||
$out .= $s[$count]; | |||
break; | |||
case 'f': | |||
$out .= chr(0x0C); | |||
break; | |||
case 'b': | |||
$out .= chr(0x08); | |||
break; | |||
case 't': | |||
$out .= chr(0x09); | |||
break; | |||
case 'r': | |||
$out .= chr(0x0D); | |||
break; | |||
case 'n': | |||
$out .= chr(0x0A); | |||
break; | |||
case "\r": | |||
if ($count != $n-1 && $s[$count+1] == "\n") | |||
$count++; | |||
break; | |||
case "\n": | |||
break; | |||
default: | |||
// Octal-Values | |||
if (ord($s[$count]) >= ord('0') && | |||
ord($s[$count]) <= ord('9')) { | |||
$oct = ''. $s[$count]; | |||
if (ord($s[$count+1]) >= ord('0') && | |||
ord($s[$count+1]) <= ord('9')) { | |||
$oct .= $s[++$count]; | |||
if (ord($s[$count+1]) >= ord('0') && | |||
ord($s[$count+1]) <= ord('9')) { | |||
$oct .= $s[++$count]; | |||
} | |||
} | |||
$out .= chr(octdec($oct)); | |||
} else { | |||
$out .= $s[$count]; | |||
} | |||
} | |||
} | |||
} | |||
return $out; | |||
} | |||
/** | |||
* Hexadecimal to string | |||
* | |||
* @param string $data | |||
* @return string | |||
*/ | |||
public function hex2str($data) | |||
{ | |||
$data = preg_replace('/[^0-9A-Fa-f]/', '', rtrim($data, '>')); | |||
if ((strlen($data) % 2) == 1) { | |||
$data .= '0'; | |||
} | |||
return pack('H*', $data); | |||
} | |||
/** | |||
* String to hexadecimal | |||
* | |||
* @param string $str | |||
* @return string | |||
*/ | |||
public function str2hex($str) | |||
{ | |||
return current(unpack('H*', $str)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,347 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
if (!class_exists('pdf_parser')) { | |||
require_once('pdf_parser.php'); | |||
} | |||
/** | |||
* Class fpdi_pdf_parser | |||
*/ | |||
class fpdi_pdf_parser extends pdf_parser | |||
{ | |||
/** | |||
* Pages | |||
* | |||
* Index begins at 0 | |||
* | |||
* @var array | |||
*/ | |||
protected $_pages; | |||
/** | |||
* Page count | |||
* | |||
* @var integer | |||
*/ | |||
protected $_pageCount; | |||
/** | |||
* Current page number | |||
* | |||
* @var integer | |||
*/ | |||
public $pageNo; | |||
/** | |||
* PDF version of imported document | |||
* | |||
* @var string | |||
*/ | |||
public $_pdfVersion; | |||
/** | |||
* Available BoxTypes | |||
* | |||
* @var array | |||
*/ | |||
public $availableBoxes = array('/MediaBox', '/CropBox', '/BleedBox', '/TrimBox', '/ArtBox'); | |||
/** | |||
* The constructor. | |||
* | |||
* @param string $filename The source filename | |||
*/ | |||
public function __construct($filename) | |||
{ | |||
parent::__construct($filename); | |||
// resolve Pages-Dictonary | |||
$pages = $this->resolveObject($this->_root[1][1]['/Pages']); | |||
// Read pages | |||
$this->_readPages($pages, $this->_pages); | |||
// count pages; | |||
$this->_pageCount = count($this->_pages); | |||
} | |||
/** | |||
* Get page count from source file. | |||
* | |||
* @return int | |||
*/ | |||
public function getPageCount() | |||
{ | |||
return $this->_pageCount; | |||
} | |||
/** | |||
* Set the page number. | |||
* | |||
* @param int $pageNo Page number to use | |||
* @throws InvalidArgumentException | |||
*/ | |||
public function setPageNo($pageNo) | |||
{ | |||
$pageNo = ((int) $pageNo) - 1; | |||
if ($pageNo < 0 || $pageNo >= $this->getPageCount()) { | |||
throw new InvalidArgumentException('Invalid page number!'); | |||
} | |||
$this->pageNo = $pageNo; | |||
} | |||
/** | |||
* Get page-resources from current page | |||
* | |||
* @return array|boolean | |||
*/ | |||
public function getPageResources() | |||
{ | |||
return $this->_getPageResources($this->_pages[$this->pageNo]); | |||
} | |||
/** | |||
* Get page-resources from a /Page dictionary. | |||
* | |||
* @param array $obj Array of pdf-data | |||
* @return array|boolean | |||
*/ | |||
protected function _getPageResources($obj) | |||
{ | |||
$obj = $this->resolveObject($obj); | |||
// If the current object has a resources | |||
// dictionary associated with it, we use | |||
// it. Otherwise, we move back to its | |||
// parent object. | |||
if (isset($obj[1][1]['/Resources'])) { | |||
$res = $this->resolveObject($obj[1][1]['/Resources']); | |||
if ($res[0] == pdf_parser::TYPE_OBJECT) | |||
return $res[1]; | |||
return $res; | |||
} | |||
if (!isset($obj[1][1]['/Parent'])) { | |||
return false; | |||
} | |||
$res = $this->_getPageResources($obj[1][1]['/Parent']); | |||
if ($res[0] == pdf_parser::TYPE_OBJECT) | |||
return $res[1]; | |||
return $res; | |||
} | |||
/** | |||
* Get content of current page. | |||
* | |||
* If /Contents is an array, the streams are concatenated | |||
* | |||
* @return string | |||
*/ | |||
public function getContent() | |||
{ | |||
$buffer = ''; | |||
if (isset($this->_pages[$this->pageNo][1][1]['/Contents'])) { | |||
$contents = $this->_getPageContent($this->_pages[$this->pageNo][1][1]['/Contents']); | |||
foreach ($contents AS $tmpContent) { | |||
$buffer .= $this->_unFilterStream($tmpContent) . ' '; | |||
} | |||
} | |||
return $buffer; | |||
} | |||
/** | |||
* Resolve all content objects. | |||
* | |||
* @param array $contentRef | |||
* @return array | |||
*/ | |||
protected function _getPageContent($contentRef) | |||
{ | |||
$contents = array(); | |||
if ($contentRef[0] == pdf_parser::TYPE_OBJREF) { | |||
$content = $this->resolveObject($contentRef); | |||
if ($content[1][0] == pdf_parser::TYPE_ARRAY) { | |||
$contents = $this->_getPageContent($content[1]); | |||
} else { | |||
$contents[] = $content; | |||
} | |||
} else if ($contentRef[0] == pdf_parser::TYPE_ARRAY) { | |||
foreach ($contentRef[1] AS $tmp_content_ref) { | |||
$contents = array_merge($contents, $this->_getPageContent($tmp_content_ref)); | |||
} | |||
} | |||
return $contents; | |||
} | |||
/** | |||
* Get a boundary box from a page | |||
* | |||
* Array format is same as used by FPDF_TPL. | |||
* | |||
* @param array $page a /Page dictionary | |||
* @param string $boxIndex Type of box {see {@link $availableBoxes}) | |||
* @param float Scale factor from user space units to points | |||
* | |||
* @return array|boolean | |||
*/ | |||
protected function _getPageBox($page, $boxIndex, $k) | |||
{ | |||
$page = $this->resolveObject($page); | |||
$box = null; | |||
if (isset($page[1][1][$boxIndex])) { | |||
$box = $page[1][1][$boxIndex]; | |||
} | |||
if (!is_null($box) && $box[0] == pdf_parser::TYPE_OBJREF) { | |||
$tmp_box = $this->resolveObject($box); | |||
$box = $tmp_box[1]; | |||
} | |||
if (!is_null($box) && $box[0] == pdf_parser::TYPE_ARRAY) { | |||
$b = $box[1]; | |||
return array( | |||
'x' => $b[0][1] / $k, | |||
'y' => $b[1][1] / $k, | |||
'w' => abs($b[0][1] - $b[2][1]) / $k, | |||
'h' => abs($b[1][1] - $b[3][1]) / $k, | |||
'llx' => min($b[0][1], $b[2][1]) / $k, | |||
'lly' => min($b[1][1], $b[3][1]) / $k, | |||
'urx' => max($b[0][1], $b[2][1]) / $k, | |||
'ury' => max($b[1][1], $b[3][1]) / $k, | |||
); | |||
} else if (!isset($page[1][1]['/Parent'])) { | |||
return false; | |||
} else { | |||
return $this->_getPageBox($this->resolveObject($page[1][1]['/Parent']), $boxIndex, $k); | |||
} | |||
} | |||
/** | |||
* Get all page boundary boxes by page number | |||
* | |||
* @param int $pageNo The page number | |||
* @param float $k Scale factor from user space units to points | |||
* @return array | |||
* @throws InvalidArgumentException | |||
*/ | |||
public function getPageBoxes($pageNo, $k) | |||
{ | |||
if (!isset($this->_pages[$pageNo - 1])) { | |||
throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.'); | |||
} | |||
return $this->_getPageBoxes($this->_pages[$pageNo - 1], $k); | |||
} | |||
/** | |||
* Get all boxes from /Page dictionary | |||
* | |||
* @param array $page A /Page dictionary | |||
* @param float $k Scale factor from user space units to points | |||
* @return array | |||
*/ | |||
protected function _getPageBoxes($page, $k) | |||
{ | |||
$boxes = array(); | |||
foreach($this->availableBoxes AS $box) { | |||
if ($_box = $this->_getPageBox($page, $box, $k)) { | |||
$boxes[$box] = $_box; | |||
} | |||
} | |||
return $boxes; | |||
} | |||
/** | |||
* Get the page rotation by page number | |||
* | |||
* @param integer $pageNo | |||
* @throws InvalidArgumentException | |||
* @return array | |||
*/ | |||
public function getPageRotation($pageNo) | |||
{ | |||
if (!isset($this->_pages[$pageNo - 1])) { | |||
throw new InvalidArgumentException('Page ' . $pageNo . ' does not exists.'); | |||
} | |||
return $this->_getPageRotation($this->_pages[$pageNo - 1]); | |||
} | |||
/** | |||
* Get the rotation value of a page | |||
* | |||
* @param array $obj A /Page dictionary | |||
* @return array|bool | |||
*/ | |||
protected function _getPageRotation($obj) | |||
{ | |||
$obj = $this->resolveObject($obj); | |||
if (isset($obj[1][1]['/Rotate'])) { | |||
$res = $this->resolveObject($obj[1][1]['/Rotate']); | |||
if ($res[0] == pdf_parser::TYPE_OBJECT) | |||
return $res[1]; | |||
return $res; | |||
} | |||
if (!isset($obj[1][1]['/Parent'])) { | |||
return false; | |||
} | |||
$res = $this->_getPageRotation($obj[1][1]['/Parent']); | |||
if ($res[0] == pdf_parser::TYPE_OBJECT) | |||
return $res[1]; | |||
return $res; | |||
} | |||
/** | |||
* Read all pages | |||
* | |||
* @param array $pages /Pages dictionary | |||
* @param array $result The result array | |||
* @throws Exception | |||
*/ | |||
protected function _readPages(&$pages, &$result) | |||
{ | |||
// Get the kids dictionary | |||
$_kids = $this->resolveObject($pages[1][1]['/Kids']); | |||
if (!is_array($_kids)) { | |||
throw new Exception('Cannot find /Kids in current /Page-Dictionary'); | |||
} | |||
if ($_kids[0] === self::TYPE_OBJECT) { | |||
$_kids = $_kids[1]; | |||
} | |||
$kids = $_kids[1]; | |||
foreach ($kids as $v) { | |||
$pg = $this->resolveObject($v); | |||
if ($pg[1][1]['/Type'][1] === '/Pages') { | |||
// If one of the kids is an embedded | |||
// /Pages array, resolve it as well. | |||
$this->_readPages($pg, $result); | |||
} else { | |||
$result[] = $pg; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,144 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* Class pdf_context | |||
*/ | |||
class pdf_context | |||
{ | |||
/** | |||
* Mode | |||
* | |||
* @var integer 0 = file | 1 = string | |||
*/ | |||
protected $_mode = 0; | |||
/** | |||
* @var resource|string | |||
*/ | |||
public $file; | |||
/** | |||
* @var string | |||
*/ | |||
public $buffer; | |||
/** | |||
* @var integer | |||
*/ | |||
public $offset; | |||
/** | |||
* @var integer | |||
*/ | |||
public $length; | |||
/** | |||
* @var array | |||
*/ | |||
public $stack; | |||
/** | |||
* The constructor | |||
* | |||
* @param resource $f | |||
*/ | |||
public function __construct(&$f) | |||
{ | |||
$this->file =& $f; | |||
if (is_string($this->file)) | |||
$this->_mode = 1; | |||
$this->reset(); | |||
} | |||
/** | |||
* Get the position in the file stream | |||
* | |||
* @return int | |||
*/ | |||
public function getPos() | |||
{ | |||
if ($this->_mode == 0) { | |||
return ftell($this->file); | |||
} else { | |||
return 0; | |||
} | |||
} | |||
/** | |||
* Reset the position in the file stream. | |||
* | |||
* Optionally move the file pointer to a new location and reset the buffered data. | |||
* | |||
* @param null $pos | |||
* @param int $l | |||
*/ | |||
public function reset($pos = null, $l = 100) | |||
{ | |||
if ($this->_mode == 0) { | |||
if (!is_null($pos)) { | |||
fseek ($this->file, $pos); | |||
} | |||
$this->buffer = $l > 0 ? fread($this->file, $l) : ''; | |||
$this->length = strlen($this->buffer); | |||
if ($this->length < $l) | |||
$this->increaseLength($l - $this->length); | |||
} else { | |||
$this->buffer = $this->file; | |||
$this->length = strlen($this->buffer); | |||
} | |||
$this->offset = 0; | |||
$this->stack = array(); | |||
} | |||
/** | |||
* Make sure that there is at least one character beyond the current offset in the buffer. | |||
* | |||
* To prevent the tokenizer from attempting to access data that does not exist. | |||
* | |||
* @return bool | |||
*/ | |||
public function ensureContent() | |||
{ | |||
if ($this->offset >= $this->length - 1) { | |||
return $this->increaseLength(); | |||
} else { | |||
return true; | |||
} | |||
} | |||
/** | |||
* Forcefully read more data into the buffer | |||
* | |||
* @param int $l | |||
* @return bool | |||
*/ | |||
public function increaseLength($l = 100) | |||
{ | |||
if ($this->_mode == 0 && feof($this->file)) { | |||
return false; | |||
} else if ($this->_mode == 0) { | |||
$totalLength = $this->length + $l; | |||
do { | |||
$toRead = $totalLength - $this->length; | |||
if ($toRead < 1) | |||
break; | |||
$this->buffer .= fread($this->file, $toRead); | |||
} while ((($this->length = strlen($this->buffer)) != $totalLength) && !feof($this->file)); | |||
return true; | |||
} else { | |||
return false; | |||
} | |||
} | |||
} |
@@ -0,0 +1,925 @@ | |||
<?php | |||
/** | |||
* This file is part of FPDI | |||
* | |||
* @package FPDI | |||
* @copyright Copyright (c) 2015 Setasign - Jan Slabon (http://www.setasign.com) | |||
* @license http://opensource.org/licenses/mit-license The MIT License | |||
* @version 1.6.1 | |||
*/ | |||
/** | |||
* Class pdf_parser | |||
*/ | |||
class pdf_parser | |||
{ | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_NULL = 0; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_NUMERIC = 1; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_TOKEN = 2; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_HEX = 3; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_STRING = 4; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_DICTIONARY = 5; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_ARRAY = 6; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_OBJDEC = 7; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_OBJREF = 8; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_OBJECT = 9; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_STREAM = 10; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_BOOLEAN = 11; | |||
/** | |||
* Type constant | |||
* | |||
* @var integer | |||
*/ | |||
const TYPE_REAL = 12; | |||
/** | |||
* Define the amount of byte in which the initial keyword of a PDF document should be searched. | |||
* | |||
* @var int | |||
*/ | |||
static public $searchForStartxrefLength = 5500; | |||
/** | |||
* Filename | |||
* | |||
* @var string | |||
*/ | |||
public $filename; | |||
/** | |||
* File resource | |||
* | |||
* @var resource | |||
*/ | |||
protected $_f; | |||
/** | |||
* PDF Context | |||
* | |||
* @var pdf_context | |||
*/ | |||
protected $_c; | |||
/** | |||
* xref-Data | |||
* | |||
* @var array | |||
*/ | |||
protected $_xref; | |||
/** | |||
* Data of the Root object | |||
* | |||
* @var array | |||
*/ | |||
protected $_root; | |||
/** | |||
* PDF version of the loaded document | |||
* | |||
* @var string | |||
*/ | |||
protected $_pdfVersion; | |||
/** | |||
* For reading encrypted documents and xref/object streams are in use | |||
* | |||
* @var boolean | |||
*/ | |||
protected $_readPlain = true; | |||
/** | |||
* The current read object | |||
* | |||
* @var array | |||
*/ | |||
protected $_currentObj; | |||
/** | |||
* Constructor | |||
* | |||
* @param string $filename Source filename | |||
* @throws InvalidArgumentException | |||
*/ | |||
public function __construct($filename) | |||
{ | |||
$this->filename = $filename; | |||
$this->_f = @fopen($this->filename, 'rb'); | |||
if (!$this->_f) { | |||
throw new InvalidArgumentException(sprintf('Cannot open %s !', $filename)); | |||
} | |||
$this->getPdfVersion(); | |||
if (!class_exists('pdf_context')) { | |||
require_once('pdf_context.php'); | |||
} | |||
$this->_c = new pdf_context($this->_f); | |||
// Read xref-Data | |||
$this->_xref = array(); | |||
$this->_readXref($this->_xref, $this->_findXref()); | |||
// Check for Encryption | |||
$this->getEncryption(); | |||
// Read root | |||
$this->_readRoot(); | |||
} | |||
/** | |||
* Destructor | |||
*/ | |||
public function __destruct() | |||
{ | |||
$this->closeFile(); | |||
} | |||
/** | |||
* Close the opened file | |||
*/ | |||
public function closeFile() | |||
{ | |||
if (isset($this->_f) && is_resource($this->_f)) { | |||
fclose($this->_f); | |||
unset($this->_f); | |||
} | |||
} | |||
/** | |||
* Check Trailer for Encryption | |||
* | |||
* @throws Exception | |||
*/ | |||
public function getEncryption() | |||
{ | |||
if (isset($this->_xref['trailer'][1]['/Encrypt'])) { | |||
throw new Exception('File is encrypted!'); | |||
} | |||
} | |||
/** | |||
* Get PDF-Version | |||
* | |||
* @return string | |||
*/ | |||
public function getPdfVersion() | |||
{ | |||
if ($this->_pdfVersion === null) { | |||
fseek($this->_f, 0); | |||
preg_match('/\d\.\d/', fread($this->_f, 16), $m); | |||
if (isset($m[0])) | |||
$this->_pdfVersion = $m[0]; | |||
} | |||
return $this->_pdfVersion; | |||
} | |||
/** | |||
* Read the /Root dictionary | |||
*/ | |||
protected function _readRoot() | |||
{ | |||
if ($this->_xref['trailer'][1]['/Root'][0] != self::TYPE_OBJREF) { | |||
throw new Exception('Wrong Type of Root-Element! Must be an indirect reference'); | |||
} | |||
$this->_root = $this->resolveObject($this->_xref['trailer'][1]['/Root']); | |||
} | |||
/** | |||
* Find the xref table | |||
* | |||
* @return integer | |||
* @throws Exception | |||
*/ | |||
protected function _findXref() | |||
{ | |||
$toRead = self::$searchForStartxrefLength; | |||
$stat = fseek($this->_f, -$toRead, SEEK_END); | |||
if ($stat === -1) { | |||
fseek($this->_f, 0); | |||
} | |||
$data = fread($this->_f, $toRead); | |||
$keywordPos = strpos(strrev($data), strrev('startxref')); | |||
if (false === $keywordPos) { | |||
$keywordPos = strpos(strrev($data), strrev('startref')); | |||
} | |||
if (false === $keywordPos) { | |||
throw new Exception('Unable to find "startxref" keyword.'); | |||
} | |||
$pos = strlen($data) - $keywordPos; | |||
$data = substr($data, $pos); | |||
if (!preg_match('/\s*(\d+).*$/s', $data, $matches)) { | |||
throw new Exception('Unable to find pointer to xref table.'); | |||
} | |||
return (int) $matches[1]; | |||
} | |||
/** | |||
* Read the xref table | |||
* | |||
* @param array $result Array of xref table entries | |||
* @param integer $offset of xref table | |||
* @return boolean | |||
* @throws Exception | |||
*/ | |||
protected function _readXref(&$result, $offset) | |||
{ | |||
$tempPos = $offset - min(20, $offset); | |||
fseek($this->_f, $tempPos); // set some bytes backwards to fetch corrupted docs | |||
$data = fread($this->_f, 100); | |||
$xrefPos = strrpos($data, 'xref'); | |||
if ($xrefPos === false) { | |||
$this->_c->reset($offset); | |||
$xrefStreamObjDec = $this->_readValue($this->_c); | |||
if (is_array($xrefStreamObjDec) && isset($xrefStreamObjDec[0]) && $xrefStreamObjDec[0] == self::TYPE_OBJDEC) { | |||
throw new Exception( | |||
sprintf( | |||
'This document (%s) probably uses a compression technique which is not supported by the ' . | |||
'free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)', | |||
$this->filename | |||
) | |||
); | |||
} else { | |||
throw new Exception('Unable to find xref table.'); | |||
} | |||
} | |||
if (!isset($result['xrefLocation'])) { | |||
$result['xrefLocation'] = $tempPos + $xrefPos; | |||
$result['maxObject'] = 0; | |||
} | |||
$cycles = -1; | |||
$bytesPerCycle = 100; | |||
fseek($this->_f, $tempPos = $tempPos + $xrefPos + 4); // set the handle directly after the "xref"-keyword | |||
$data = fread($this->_f, $bytesPerCycle); | |||
while (($trailerPos = strpos($data, 'trailer', max($bytesPerCycle * $cycles++, 0))) === false && !feof($this->_f)) { | |||
$data .= fread($this->_f, $bytesPerCycle); | |||
} | |||
if ($trailerPos === false) { | |||
throw new Exception('Trailer keyword not found after xref table'); | |||
} | |||
$data = ltrim(substr($data, 0, $trailerPos)); | |||
// get Line-Ending | |||
$found = preg_match_all("/(\r\n|\n|\r)/", substr($data, 0, 100), $m); // check the first 100 bytes for line breaks | |||
if ($found === 0) { | |||
throw new Exception('Xref table seems to be corrupted.'); | |||
} | |||
$differentLineEndings = count(array_unique($m[0])); | |||
if ($differentLineEndings > 1) { | |||
$lines = preg_split("/(\r\n|\n|\r)/", $data, -1, PREG_SPLIT_NO_EMPTY); | |||
} else { | |||
$lines = explode($m[0][0], $data); | |||
} | |||
$data = $differentLineEndings = $m = null; | |||
unset($data, $differentLineEndings, $m); | |||
$linesCount = count($lines); | |||
$start = 1; | |||
for ($i = 0; $i < $linesCount; $i++) { | |||
$line = trim($lines[$i]); | |||
if ($line) { | |||
$pieces = explode(' ', $line); | |||
$c = count($pieces); | |||
switch($c) { | |||
case 2: | |||
$start = (int)$pieces[0]; | |||
$end = $start + (int)$pieces[1]; | |||
if ($end > $result['maxObject']) | |||
$result['maxObject'] = $end; | |||
break; | |||
case 3: | |||
if (!isset($result['xref'][$start])) | |||
$result['xref'][$start] = array(); | |||
if (!array_key_exists($gen = (int) $pieces[1], $result['xref'][$start])) { | |||
$result['xref'][$start][$gen] = $pieces[2] == 'n' ? (int) $pieces[0] : null; | |||
} | |||
$start++; | |||
break; | |||
default: | |||
throw new Exception('Unexpected data in xref table'); | |||
} | |||
} | |||
} | |||
$lines = $pieces = $line = $start = $end = $gen = null; | |||
unset($lines, $pieces, $line, $start, $end, $gen); | |||
$this->_c->reset($tempPos + $trailerPos + 7); | |||
$trailer = $this->_readValue($this->_c); | |||
if (!isset($result['trailer'])) { | |||
$result['trailer'] = $trailer; | |||
} | |||
if (isset($trailer[1]['/Prev'])) { | |||
$this->_readXref($result, $trailer[1]['/Prev'][1]); | |||
} | |||
$trailer = null; | |||
unset($trailer); | |||
return true; | |||
} | |||
/** | |||
* Reads a PDF value | |||
* | |||
* @param pdf_context $c | |||
* @param string $token A token | |||
* @return mixed | |||
* @throws Exception | |||
*/ | |||
protected function _readValue(&$c, $token = null) | |||
{ | |||
if (is_null($token)) { | |||
$token = $this->_readToken($c); | |||
} | |||
if ($token === false) { | |||
return false; | |||
} | |||
switch ($token) { | |||
case '<': | |||
// This is a hex string. | |||
// Read the value, then the terminator | |||
$pos = $c->offset; | |||
while(1) { | |||
$match = strpos($c->buffer, '>', $pos); | |||
// If you can't find it, try | |||
// reading more data from the stream | |||
if ($match === false) { | |||
if (!$c->increaseLength()) { | |||
return false; | |||
} else { | |||
continue; | |||
} | |||
} | |||
$result = substr($c->buffer, $c->offset, $match - $c->offset); | |||
$c->offset = $match + 1; | |||
return array (self::TYPE_HEX, $result); | |||
} | |||
break; | |||
case '<<': | |||
// This is a dictionary. | |||
$result = array(); | |||
// Recurse into this function until we reach | |||
// the end of the dictionary. | |||
while (($key = $this->_readToken($c)) !== '>>') { | |||
if ($key === false) { | |||
return false; | |||
} | |||
if (($value = $this->_readValue($c)) === false) { | |||
return false; | |||
} | |||
// Catch missing value | |||
if ($value[0] == self::TYPE_TOKEN && $value[1] == '>>') { | |||
$result[$key] = array(self::TYPE_NULL); | |||
break; | |||
} | |||
$result[$key] = $value; | |||
} | |||
return array (self::TYPE_DICTIONARY, $result); | |||
case '[': | |||
// This is an array. | |||
$result = array(); | |||
// Recurse into this function until we reach | |||
// the end of the array. | |||
while (($token = $this->_readToken($c)) !== ']') { | |||
if ($token === false) { | |||
return false; | |||
} | |||
if (($value = $this->_readValue($c, $token)) === false) { | |||
return false; | |||
} | |||
$result[] = $value; | |||
} | |||
return array (self::TYPE_ARRAY, $result); | |||
case '(': | |||
// This is a string | |||
$pos = $c->offset; | |||
$openBrackets = 1; | |||
do { | |||
for (; $openBrackets != 0 && $pos < $c->length; $pos++) { | |||
switch (ord($c->buffer[$pos])) { | |||
case 0x28: // '(' | |||
$openBrackets++; | |||
break; | |||
case 0x29: // ')' | |||
$openBrackets--; | |||
break; | |||
case 0x5C: // backslash | |||
$pos++; | |||
} | |||
} | |||
} while($openBrackets != 0 && $c->increaseLength()); | |||
$result = substr($c->buffer, $c->offset, $pos - $c->offset - 1); | |||
$c->offset = $pos; | |||
return array (self::TYPE_STRING, $result); | |||
case 'stream': | |||
$tempPos = $c->getPos() - strlen($c->buffer); | |||
$tempOffset = $c->offset; | |||
$c->reset($startPos = $tempPos + $tempOffset); | |||
// Find the first "newline" | |||
while ($c->buffer[0] !== chr(10) && $c->buffer[0] !== chr(13)) { | |||
$c->reset(++$startPos); | |||
if ($c->ensureContent() === false) { | |||
throw new Exception( | |||
'Unable to parse stream data. No newline followed the stream keyword.' | |||
); | |||
} | |||
} | |||
$e = 0; // ensure line breaks in front of the stream | |||
if ($c->buffer[0] == chr(10) || $c->buffer[0] == chr(13)) | |||
$e++; | |||
if ($c->buffer[1] == chr(10) && $c->buffer[0] != chr(10)) | |||
$e++; | |||
if ($this->_currentObj[1][1]['/Length'][0] == self::TYPE_OBJREF) { | |||
$tmpLength = $this->resolveObject($this->_currentObj[1][1]['/Length']); | |||
$length = $tmpLength[1][1]; | |||
} else { | |||
$length = $this->_currentObj[1][1]['/Length'][1]; | |||
} | |||
if ($length > 0) { | |||
$c->reset($startPos + $e, $length); | |||
$v = $c->buffer; | |||
} else { | |||
$v = ''; | |||
} | |||
$c->reset($startPos + $e + $length); | |||
$endstream = $this->_readToken($c); | |||
if ($endstream != 'endstream') { | |||
$c->reset($startPos + $e + $length + 9); // 9 = strlen("endstream") | |||
// We don't throw an error here because the next | |||
// round trip will start at a new offset | |||
} | |||
return array(self::TYPE_STREAM, $v); | |||
default: | |||
if (is_numeric($token)) { | |||
// A numeric token. Make sure that | |||
// it is not part of something else. | |||
if (($tok2 = $this->_readToken($c)) !== false) { | |||
if (is_numeric($tok2)) { | |||
// Two numeric tokens in a row. | |||
// In this case, we're probably in | |||
// front of either an object reference | |||
// or an object specification. | |||
// Determine the case and return the data | |||
if (($tok3 = $this->_readToken($c)) !== false) { | |||
switch ($tok3) { | |||
case 'obj': | |||
return array(self::TYPE_OBJDEC, (int)$token, (int)$tok2); | |||
case 'R': | |||
return array(self::TYPE_OBJREF, (int)$token, (int)$tok2); | |||
} | |||
// If we get to this point, that numeric value up | |||
// there was just a numeric value. Push the extra | |||
// tokens back into the stack and return the value. | |||
array_push($c->stack, $tok3); | |||
} | |||
} | |||
array_push($c->stack, $tok2); | |||
} | |||
if ($token === (string)((int)$token)) | |||
return array(self::TYPE_NUMERIC, (int)$token); | |||
else | |||
return array(self::TYPE_REAL, (float)$token); | |||
} else if ($token == 'true' || $token == 'false') { | |||
return array(self::TYPE_BOOLEAN, $token == 'true'); | |||
} else if ($token == 'null') { | |||
return array(self::TYPE_NULL); | |||
} else { | |||
// Just a token. Return it. | |||
return array(self::TYPE_TOKEN, $token); | |||
} | |||
} | |||
} | |||
/** | |||
* Resolve an object | |||
* | |||
* @param array $objSpec The object-data | |||
* @return array|boolean | |||
* @throws Exception | |||
*/ | |||
public function resolveObject($objSpec) | |||
{ | |||
$c = $this->_c; | |||
// Exit if we get invalid data | |||
if (!is_array($objSpec)) { | |||
return false; | |||
} | |||
if ($objSpec[0] == self::TYPE_OBJREF) { | |||
// This is a reference, resolve it | |||
if (isset($this->_xref['xref'][$objSpec[1]][$objSpec[2]])) { | |||
// Save current file position | |||
// This is needed if you want to resolve | |||
// references while you're reading another object | |||
// (e.g.: if you need to determine the length | |||
// of a stream) | |||
$oldPos = $c->getPos(); | |||
// Reposition the file pointer and | |||
// load the object header. | |||
$c->reset($this->_xref['xref'][$objSpec[1]][$objSpec[2]]); | |||
$header = $this->_readValue($c); | |||
if ($header[0] != self::TYPE_OBJDEC || $header[1] != $objSpec[1] || $header[2] != $objSpec[2]) { | |||
$toSearchFor = $objSpec[1] . ' ' . $objSpec[2] . ' obj'; | |||
if (preg_match('/' . $toSearchFor . '/', $c->buffer)) { | |||
$c->offset = strpos($c->buffer, $toSearchFor) + strlen($toSearchFor); | |||
// reset stack | |||
$c->stack = array(); | |||
} else { | |||
throw new Exception( | |||
sprintf("Unable to find object (%s, %s) at expected location.", $objSpec[1], $objSpec[2]) | |||
); | |||
} | |||
} | |||
// If we're being asked to store all the information | |||
// about the object, we add the object ID and generation | |||
// number for later use | |||
$result = array ( | |||
self::TYPE_OBJECT, | |||
'obj' => $objSpec[1], | |||
'gen' => $objSpec[2] | |||
); | |||
$this->_currentObj =& $result; | |||
// Now simply read the object data until | |||
// we encounter an end-of-object marker | |||
while (true) { | |||
$value = $this->_readValue($c); | |||
if ($value === false || count($result) > 4) { | |||
// in this case the parser couldn't find an "endobj" so we break here | |||
break; | |||
} | |||
if ($value[0] == self::TYPE_TOKEN && $value[1] === 'endobj') { | |||
break; | |||
} | |||
$result[] = $value; | |||
} | |||
$c->reset($oldPos); | |||
if (isset($result[2][0]) && $result[2][0] == self::TYPE_STREAM) { | |||
$result[0] = self::TYPE_STREAM; | |||
} | |||
} else { | |||
throw new Exception( | |||
sprintf("Unable to find object (%s, %s) at expected location.", $objSpec[1], $objSpec[2]) | |||
); | |||
} | |||
return $result; | |||
} else { | |||
return $objSpec; | |||
} | |||
} | |||
/** | |||
* Reads a token from the context | |||
* | |||
* @param pdf_context $c | |||
* @return mixed | |||
*/ | |||
protected function _readToken($c) | |||
{ | |||
// If there is a token available | |||
// on the stack, pop it out and | |||
// return it. | |||
if (count($c->stack)) { | |||
return array_pop($c->stack); | |||
} | |||
// Strip away any whitespace | |||
do { | |||
if (!$c->ensureContent()) { | |||
return false; | |||
} | |||
$c->offset += strspn($c->buffer, "\x20\x0A\x0C\x0D\x09\x00", $c->offset); | |||
} while ($c->offset >= $c->length - 1); | |||
// Get the first character in the stream | |||
$char = $c->buffer[$c->offset++]; | |||
switch ($char) { | |||
case '[': | |||
case ']': | |||
case '(': | |||
case ')': | |||
// This is either an array or literal string | |||
// delimiter, Return it | |||
return $char; | |||
case '<': | |||
case '>': | |||
// This could either be a hex string or | |||
// dictionary delimiter. Determine the | |||
// appropriate case and return the token | |||
if ($c->buffer[$c->offset] == $char) { | |||
if (!$c->ensureContent()) { | |||
return false; | |||
} | |||
$c->offset++; | |||
return $char . $char; | |||
} else { | |||
return $char; | |||
} | |||
case '%': | |||
// This is a comment - jump over it! | |||
$pos = $c->offset; | |||
while(1) { | |||
$match = preg_match("/(\r\n|\r|\n)/", $c->buffer, $m, PREG_OFFSET_CAPTURE, $pos); | |||
if ($match === 0) { | |||
if (!$c->increaseLength()) { | |||
return false; | |||
} else { | |||
continue; | |||
} | |||
} | |||
$c->offset = $m[0][1] + strlen($m[0][0]); | |||
return $this->_readToken($c); | |||
} | |||
default: | |||
// This is "another" type of token (probably | |||
// a dictionary entry or a numeric value) | |||
// Find the end and return it. | |||
if (!$c->ensureContent()) { | |||
return false; | |||
} | |||
while(1) { | |||
// Determine the length of the token | |||
$pos = strcspn($c->buffer, "\x20%[]<>()/\x0A\x0C\x0D\x09\x00", $c->offset); | |||
if ($c->offset + $pos <= $c->length - 1) { | |||
break; | |||
} else { | |||
// If the script reaches this point, | |||
// the token may span beyond the end | |||
// of the current buffer. Therefore, | |||
// we increase the size of the buffer | |||
// and try again--just to be safe. | |||
$c->increaseLength(); | |||
} | |||
} | |||
$result = substr($c->buffer, $c->offset - 1, $pos + 1); | |||
$c->offset += $pos; | |||
return $result; | |||
} | |||
} | |||
/** | |||
* Un-filter a stream object | |||
* | |||
* @param array $obj | |||
* @return string | |||
* @throws Exception | |||
*/ | |||
protected function _unFilterStream($obj) | |||
{ | |||
$filters = array(); | |||
if (isset($obj[1][1]['/Filter'])) { | |||
$filter = $obj[1][1]['/Filter']; | |||
if ($filter[0] == pdf_parser::TYPE_OBJREF) { | |||
$tmpFilter = $this->resolveObject($filter); | |||
$filter = $tmpFilter[1]; | |||
} | |||
if ($filter[0] == pdf_parser::TYPE_TOKEN) { | |||
$filters[] = $filter; | |||
} else if ($filter[0] == pdf_parser::TYPE_ARRAY) { | |||
$filters = $filter[1]; | |||
} | |||
} | |||
$stream = $obj[2][1]; | |||
foreach ($filters AS $filter) { | |||
switch ($filter[1]) { | |||
case '/FlateDecode': | |||
case '/Fl': | |||
if (function_exists('gzuncompress')) { | |||
$oStream = $stream; | |||
$stream = (strlen($stream) > 0) ? @gzuncompress($stream) : ''; | |||
} else { | |||
throw new Exception( | |||
sprintf('To handle %s filter, please compile php with zlib support.', $filter[1]) | |||
); | |||
} | |||
if ($stream === false) { | |||
$tries = 0; | |||
while ($tries < 8 && ($stream === false || strlen($stream) < strlen($oStream))) { | |||
$oStream = substr($oStream, 1); | |||
$stream = @gzinflate($oStream); | |||
$tries++; | |||
} | |||
if ($stream === false) { | |||
throw new Exception('Error while decompressing stream.'); | |||
} | |||
} | |||
break; | |||
case '/LZWDecode': | |||
if (!class_exists('FilterLZW')) { | |||
require_once('filters/FilterLZW.php'); | |||
} | |||
$decoder = new FilterLZW(); | |||
$stream = $decoder->decode($stream); | |||
break; | |||
case '/ASCII85Decode': | |||
if (!class_exists('FilterASCII85')) { | |||
require_once('filters/FilterASCII85.php'); | |||
} | |||
$decoder = new FilterASCII85(); | |||
$stream = $decoder->decode($stream); | |||
break; | |||
case '/ASCIIHexDecode': | |||
if (!class_exists('FilterASCIIHexDecode')) { | |||
require_once('filters/FilterASCIIHexDecode.php'); | |||
} | |||
$decoder = new FilterASCIIHexDecode(); | |||
$stream = $decoder->decode($stream); | |||
break; | |||
case null: | |||
break; | |||
default: | |||
throw new Exception(sprintf('Unsupported Filter: %s', $filter[1])); | |||
} | |||
} | |||
return $stream; | |||
} | |||
} |
@@ -1,4 +1,3 @@ | |||
/tests/acceptance.conf.php | |||
/tests/smoke.conf.php | |||
/build/* | |||
/vendor/ |
@@ -1,2 +1 @@ | |||
/composer.lock | |||
/vendor | |||
/composer.lock |
@@ -93,7 +93,7 @@ class BaseYii | |||
*/ | |||
public static function getVersion() | |||
{ | |||
return '2.0.9'; | |||
return '2.0.10'; | |||
} | |||
/** |
@@ -15,11 +15,36 @@ php composer.phar self-update | |||
php composer.phar global require "fxp/composer-asset-plugin:^1.2.0" | |||
``` | |||
Then run: | |||
``` | |||
php composer.phar require "yiisoft/yii2:~2.0.9" | |||
``` | |||
Upgrade from Yii 2.0.9 | |||
---------------------- | |||
* RBAC: `getChildRoles()` method was added to `\yii\rbac\ManagerInterface`. If you've implemented your own RBAC manager | |||
you need to implement new method. | |||
* Microsoft SQL `NTEXT` data type [was marked as deprecated](https://msdn.microsoft.com/en-us/library/ms187993.aspx) in MSSQL so | |||
`\yii\db\mssql\Schema::TYPE_TEXT` was changed from `'ntext'` to `'nvarchar(max)' | |||
* Method `yii\web\Request::getBodyParams()` has been changed to pass full value of 'content-type' header to the second | |||
argument of `yii\web\RequestParserInterface::parse()`. If you create your own custom parser, which relies on `$contentType` | |||
argument, ensure to process it correctly as it may content additional data. | |||
* `yii\rest\Serializer` has been changed to return a JSON array for collection data in all cases to be consistent among pages | |||
for data that is not indexed starting by 0. If your API relies on the Serializer to return data as JSON objects indexed by | |||
PHP array keys, you should set `yii\rest\Serializer::$preserveKeys` to `true`. | |||
Upgrade from Yii 2.0.8 | |||
---------------------- | |||
* Part of code from `yii\web\User::loginByCookie()` method was moved to new `getIdentityAndDurationFromCookie()` | |||
and `removeIdentityCookie()` methods. If you override `loginByCookie()` method, update it in order use new methods. | |||
* Fixture console command syntax was changed from `yii fixture "*" -User` to `yii fixture "*, -User"`. Upgrade your | |||
scripts if necessary. | |||
@@ -56,7 +81,7 @@ Upgrade from Yii 2.0.6 | |||
* Added new requirement: ICU Data version >= 49.1. Please, ensure that your environment has ICU data installed and | |||
up to date to prevent unexpected behavior or crashes. This may not be the case on older systems e.g. running Debian Wheezy. | |||
> Tip: Use Yii2 Requirements checker for easy and fast check. Look for `requirements.php` in root of Basic and Advanced | |||
> Tip: Use Yii 2 Requirements checker for easy and fast check. Look for `requirements.php` in root of Basic and Advanced | |||
templates (howto-comment is in head of the script). | |||
* The signature of `yii\helpers\BaseInflector::transliterate()` was changed. The method is now public and has an | |||
@@ -72,7 +97,7 @@ Upgrade from Yii 2.0.6 | |||
* The context of `yii.confirm` JavaScript function was changed from `yii` object to the DOM element which triggered | |||
the event. | |||
- If you overrode the `yii.confirm` function and accessed the `yii` object through `this`, you must access it | |||
with global variable `yii` instead. | |||
@@ -118,7 +143,7 @@ Upgrade from Yii 2.0.3 | |||
* Updated dependency to `cebe/markdown` to version `1.1.x`. | |||
If you need stick with 1.0.x, you can specify that in your `composer.json` by | |||
adding the following line in the `require` section: | |||
```json | |||
"cebe/markdown": "~1.0.0", | |||
``` | |||
@@ -145,7 +170,7 @@ Upgrade from Yii 2.0.0 | |||
* Upgraded Twitter Bootstrap to [version 3.3.x](http://blog.getbootstrap.com/2014/10/29/bootstrap-3-3-0-released/). | |||
If you need to use an older version (i.e. stick with 3.2.x) you can specify that in your `composer.json` by | |||
adding the following line in the `require` section: | |||
```json | |||
"bower-asset/bootstrap": "3.2.*", | |||
``` | |||
@@ -160,13 +185,13 @@ Upgrade from Yii 2.0 RC | |||
This causes trouble because the formatter uses `Yii::$app->timeZone` as the default values for output so no timezone conversion | |||
was possible. If your timestamps are stored in the database without a timezone identifier you have to ensure they are in UTC or | |||
add a timezone identifier explicitly. | |||
* `yii\bootstrap\Collapse` is now encoding labels by default. `encode` item option and global `encodeLabels` property were | |||
introduced to disable it. Keys are no longer used as labels. You need to remove keys and use `label` item option instead. | |||
* The `yii\base\View::beforeRender()` and `yii\base\View::afterRender()` methods have two extra parameters `$viewFile` | |||
and `$params`. If you are overriding these methods, you should adjust the method signature accordingly. | |||
* If you've used `asImage` formatter i.e. `Yii::$app->formatter->asImage($value, $alt);` you should change it | |||
to `Yii::$app->formatter->asImage($value, ['alt' => $alt]);`. | |||
@@ -198,9 +223,9 @@ Upgrade from Yii 2.0 Beta | |||
} | |||
} | |||
``` | |||
It is also a good idea to upgrade composer itself to the latest version if you see any problems: | |||
``` | |||
php composer.phar self-update | |||
``` | |||
@@ -292,7 +317,7 @@ Upgrade from Yii 2.0 Beta | |||
doesn't have `cookieValidationKey` property. | |||
* `yii\rbac\PhpManager` now stores data in three separate files instead of one. In order to convert old file to | |||
new ones save the following code as `convert.php` that should be placed in the same directory your `rbac.php` is in: | |||
new ones save the following code as `convert.php` that should be placed in the same directory your `rbac.php` is in: | |||
```php | |||
<?php | |||
@@ -300,16 +325,16 @@ new ones save the following code as `convert.php` that should be placed in the s | |||
$itemsFile = 'items.php'; | |||
$assignmentsFile = 'assignments.php'; | |||
$rulesFile = 'rules.php'; | |||
$oldData = include $oldFile; | |||
function saveToFile($data, $fileName) { | |||
$out = var_export($data, true); | |||
$out = "<?php\nreturn " . $out . ";"; | |||
$out = "<?php\nreturn " . $out . ';'; | |||
$out = str_replace(['array (', ')'], ['[', ']'], $out); | |||
file_put_contents($fileName, $out); | |||
} | |||
$items = []; | |||
$assignments = []; | |||
if (isset($oldData['items'])) { | |||
@@ -323,16 +348,16 @@ new ones save the following code as `convert.php` that should be placed in the s | |||
$items[$name] = $data; | |||
} | |||
} | |||
$rules = []; | |||
if (isset($oldData['rules'])) { | |||
$rules = $oldData['rules']; | |||
} | |||
saveToFile($items, $itemsFile); | |||
saveToFile($assignments, $assignmentsFile); | |||
saveToFile($rules, $rulesFile); | |||
echo "Done!\n"; | |||
``` | |||
@@ -367,7 +392,7 @@ new ones save the following code as `convert.php` that should be placed in the s | |||
}, $duration, $dependency); | |||
``` | |||
* Due to significant changes to security you need to upgrade your code to use `\yii\base\Security` component instead of | |||
helper. If you have any data encrypted it should be re-encrypted. In order to do so you can use old security helper | |||
[as explained by @docsolver at github](https://github.com/yiisoft/yii2/issues/4461#issuecomment-50237807). | |||
@@ -464,6 +489,6 @@ new ones save the following code as `convert.php` that should be placed in the s | |||
* The signature of `View::registerJsFile()` and `View::registerCssFile()` has changed. The `$depends` and `$position` | |||
paramaters have been merged into `$options`. The new signatures are as follows: | |||
- `registerJsFile($url, $options = [], $key = null)` | |||
- `registerCssFile($url, $options = [], $key = null)` |
@@ -100,7 +100,15 @@ | |||
* - jqXHR: a jqXHR object | |||
* - textStatus: the status of the request ("success", "notmodified", "error", "timeout", "abort", or "parsererror"). | |||
*/ | |||
ajaxComplete: 'ajaxComplete' | |||
ajaxComplete: 'ajaxComplete', | |||
/** | |||
* afterInit event is triggered after yii activeForm init. | |||
* The signature of the event handler should be: | |||
* function (event) | |||
* where | |||
* - event: an Event object. | |||
*/ | |||
afterInit: 'afterInit' | |||
}; | |||
// NOTE: If you change any of these defaults, make sure you update yii\widgets\ActiveForm::getClientOptions() as well | |||
@@ -202,7 +210,7 @@ | |||
attributes: attributes, | |||
submitting: false, | |||
validated: false, | |||
target: $form.attr('target') | |||
options: getFormOptions($form) | |||
}); | |||
/** | |||
@@ -217,6 +225,8 @@ | |||
}); | |||
$form.on('submit.yiiActiveForm', methods.submitForm); | |||
} | |||
var event = $.Event(events.afterInit); | |||
$form.trigger(event); | |||
}); | |||
}, | |||
@@ -282,7 +292,11 @@ | |||
}, | |||
// validate all applicable inputs in the form | |||
validate: function () { | |||
validate: function (forceValidate) { | |||
if (forceValidate) { | |||
$(this).data('yiiActiveForm').submitting = true; | |||
} | |||
var $form = $(this), | |||
data = $form.data('yiiActiveForm'), | |||
needAjaxValidation = false, | |||
@@ -290,9 +304,10 @@ | |||
deferreds = deferredArray(), | |||
submitting = data.submitting; | |||
var event = $.Event(events.beforeValidate); | |||
$form.trigger(event, [messages, deferreds]); | |||
if (submitting) { | |||
var event = $.Event(events.beforeValidate); | |||
$form.trigger(event, [messages, deferreds]); | |||
if (event.result === false) { | |||
data.submitting = false; | |||
submitFinalize($form); | |||
@@ -302,6 +317,7 @@ | |||
// client-side validation | |||
$.each(data.attributes, function () { | |||
this.$form = $form; | |||
if (!$(this.input).is(":disabled")) { | |||
this.cancelled = false; | |||
// perform validation only if the form is being submitted or if an attribute is pending validation | |||
@@ -335,7 +351,7 @@ | |||
delete messages[i]; | |||
} | |||
} | |||
if ($.isEmptyObject(messages) && needAjaxValidation) { | |||
if (needAjaxValidation && ($.isEmptyObject(messages) || data.submitting)) { | |||
var $button = data.submitObject, | |||
extData = '&' + data.settings.ajaxParam + '=' + $form.attr('id'); | |||
if ($button && $button.length && $button.attr('name')) { | |||
@@ -542,6 +558,47 @@ | |||
return array; | |||
}; | |||
var buttonOptions = ['action', 'target', 'method', 'enctype']; | |||
/** | |||
* Returns current form options | |||
* @param $form | |||
* @returns object Object with button of form options | |||
*/ | |||
var getFormOptions = function ($form) { | |||
var attributes = {}; | |||
for (var i = 0; i < buttonOptions.length; i++) { | |||
attributes[buttonOptions[i]] = $form.attr(buttonOptions[i]); | |||
} | |||
return attributes; | |||
}; | |||
/** | |||
* Applies temporary form options related to submit button | |||
* @param $form the form jQuery object | |||
* @param $button the button jQuery object | |||
*/ | |||
var applyButtonOptions = function ($form, $button) { | |||
for (var i = 0; i < buttonOptions.length; i++) { | |||
var value = $button.attr('form' + buttonOptions[i]); | |||
if (value) { | |||
$form.attr(buttonOptions[i], value); | |||
} | |||
} | |||
}; | |||
/** | |||
* Restores original form options | |||
* @param $form the form jQuery object | |||
*/ | |||
var restoreButtonOptions = function ($form) { | |||
var data = $form.data('yiiActiveForm'); | |||
for (var i = 0; i < buttonOptions.length; i++) { | |||
$form.attr(buttonOptions[i], data.options[buttonOptions[i]] || null); | |||
} | |||
}; | |||
/** | |||
* Updates the error messages and the input containers for all applicable attributes | |||
* @param $form the form jQuery object | |||
@@ -551,6 +608,10 @@ | |||
var updateInputs = function ($form, messages, submitting) { | |||
var data = $form.data('yiiActiveForm'); | |||
if (data === undefined) { | |||
return false; | |||
} | |||
if (submitting) { | |||
var errorAttributes = []; | |||
$.each(data.attributes, function () { | |||
@@ -576,14 +637,11 @@ | |||
data.submitting = false; | |||
} else { | |||
data.validated = true; | |||
var buttonTarget = data.submitObject ? data.submitObject.attr('formtarget') : null; | |||
if (buttonTarget) { | |||
// set target attribute to form tag before submit | |||
$form.attr('target', buttonTarget); | |||
if (data.submitObject) { | |||
data.submitObject.trigger("click"); | |||
} else { | |||
$form.submit(); | |||
} | |||
$form.submit(); | |||
// restore original target attribute value | |||
$form.attr('target', data.target); | |||
} | |||
} else { | |||
$.each(data.attributes, function () { |
@@ -147,7 +147,7 @@ | |||
setSelectionColumn: function (options) { | |||
var $grid = $(this); | |||
var id = $(this).attr('id'); | |||
if (gridData.id === undefined) { | |||
if (gridData[id] === undefined) { | |||
gridData[id] = {}; | |||
} | |||
gridData[id].selectionColumn = options.name; |
@@ -41,11 +41,16 @@ | |||
* | |||
* You must call "yii.initModule()" once for the root module of all your modules. | |||
*/ | |||
yii = (function ($) { | |||
window.yii = (function ($) { | |||
var pub = { | |||
/** | |||
* List of JS or CSS URLs that can be loaded multiple times via AJAX requests. Each script can be represented | |||
* as either an absolute URL or a relative one. | |||
* List of JS or CSS URLs that can be loaded multiple times via AJAX requests. | |||
* Each item may be represented as either an absolute URL or a relative one. | |||
* Each item may contain a wildcart matching character `*`, that means one or more | |||
* any characters on the position. For example: | |||
* - `/css/*.js` will match any file ending with `.js` in the `css` directory of the current web site | |||
* - `http*://cdn.example.com/*` will match any files on domain `cdn.example.com`, loaded with HTTP or HTTPS | |||
* - `/js/myCustomScript.js?realm=*` will match file `/js/myCustomScript.js` with defined `realm` parameter | |||
*/ | |||
reloadableScripts: [], | |||
/** | |||
@@ -284,8 +289,8 @@ yii = (function ($) { | |||
for (i = 0; i < pairs.length; i++) { | |||
pair = pairs[i].split('='); | |||
var name = decodeURIComponent(pair[0]); | |||
var value = decodeURIComponent(pair[1]); | |||
var name = decodeURIComponent(pair[0].replace(/\+/g, '%20')); | |||
var value = decodeURIComponent(pair[1].replace(/\+/g, '%20')); | |||
if (name.length) { | |||
if (params[name] !== undefined) { | |||
if (!$.isArray(params[name])) { | |||
@@ -368,12 +373,41 @@ yii = (function ($) { | |||
.on('change.yii', pub.changeableSelector, handler); | |||
} | |||
function isReloadable(url) { | |||
var hostInfo = getHostInfo(); | |||
for (var i = 0; i < pub.reloadableScripts.length; i++) { | |||
var rule = pub.reloadableScripts[i]; | |||
rule = rule.charAt(0) === '/' ? hostInfo + rule : rule; | |||
var match = new RegExp("^" + escapeRegExp(rule).split('\\*').join('.*') + "$").test(url); | |||
if (match === true) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex | |||
function escapeRegExp(str) { | |||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | |||
} | |||
function getHostInfo() { | |||
return location.protocol + '//' + location.host; | |||
} | |||
function initScriptFilter() { | |||
var hostInfo = location.protocol + '//' + location.host; | |||
var hostInfo = getHostInfo(); | |||
var loadedScripts = {}; | |||
var loadedScripts = $('script[src]').map(function () { | |||
var scripts = $('script[src]').map(function () { | |||
return this.src.charAt(0) === '/' ? hostInfo + this.src : this.src; | |||
}).toArray(); | |||
for (var i = 0, len = scripts.length; i < len; i++) { | |||
loadedScripts[scripts[i]] = true; | |||
} | |||
$.ajaxPrefilter('script', function (options, originalOptions, xhr) { | |||
if (options.dataType == 'jsonp') { | |||
@@ -381,22 +415,40 @@ yii = (function ($) { | |||
} | |||
var url = options.url.charAt(0) === '/' ? hostInfo + options.url : options.url; | |||
if ($.inArray(url, loadedScripts) === -1) { | |||
loadedScripts.push(url); | |||
} else { | |||
var isReloadable = $.inArray(url, $.map(pub.reloadableScripts, function (script) { | |||
return script.charAt(0) === '/' ? hostInfo + script : script; | |||
})) !== -1; | |||
if (!isReloadable) { | |||
if (url in loadedScripts) { | |||
var item = loadedScripts[url]; | |||
// If the concurrent XHR request is running and URL is not reloadable | |||
if (item !== true && !isReloadable(url)) { | |||
// Abort the current XHR request when previous finished successfully | |||
item.done(function () { | |||
if (xhr && xhr.readyState !== 4) { | |||
xhr.abort(); | |||
} | |||
}); | |||
// Or abort previous XHR if the current one is loaded faster | |||
xhr.done(function () { | |||
if (item && item.readyState !== 4) { | |||
item.abort(); | |||
} | |||
}); | |||
} else if (!isReloadable(url)) { | |||
xhr.abort(); | |||
} | |||
} else { | |||
loadedScripts[url] = xhr.done(function () { | |||
loadedScripts[url] = true; | |||
}).fail(function () { | |||
delete loadedScripts[url]; | |||
}); | |||
} | |||
}); | |||
$(document).ajaxComplete(function (event, xhr, settings) { | |||
var styleSheets = []; | |||
$('link[rel=stylesheet]').each(function () { | |||
if ($.inArray(this.href, pub.reloadableScripts) !== -1) { | |||
if (isReloadable(this.href)) { | |||
return; | |||
} | |||
if ($.inArray(this.href, styleSheets) == -1) { |
@@ -383,7 +383,7 @@ yii.validation = (function ($) { | |||
return []; | |||
} | |||
var files = $(attribute.input).get(0).files; | |||
var files = $(attribute.input, attribute.$form).get(0).files; | |||
if (!files) { | |||
messages.push(options.message); | |||
return []; |
@@ -419,10 +419,9 @@ class Component extends Object | |||
* ] | |||
* ``` | |||
* | |||
* Note that a behavior class must extend from [[Behavior]]. Behavior names can be strings | |||
* or integers. If the former, they uniquely identify the behaviors. If the latter, the corresponding | |||
* behaviors are anonymous and their properties and methods will NOT be made available via the component | |||
* (however, the behaviors can still respond to the component's events). | |||
* Note that a behavior class must extend from [[Behavior]]. Behaviors can be attached using a name or anonymously. | |||
* When a name is used as the array key, using this name, the behavior can later be retrieved using [[getBehavior()]] | |||
* or be detached using [[detachBehavior()]]. Anonymous behaviors can not be retrieved or detached. | |||
* | |||
* Behaviors declared in this method will be attached to the component automatically (on demand). | |||
* |
@@ -13,7 +13,7 @@ namespace yii\base; | |||
* It encapsulates the parameters associated with an event. | |||
* The [[sender]] property describes who raises the event. | |||
* And the [[handled]] property indicates if the event is handled. | |||
* If an event handler sets [[handled]] to be true, the rest of the | |||
* If an event handler sets [[handled]] to be `true`, the rest of the | |||
* uninvoked handlers will no longer be called to handle the event. | |||
* | |||
* Additionally, when attaching an event handler, extra data may be passed | |||
@@ -31,14 +31,14 @@ class Event extends Object | |||
public $name; | |||
/** | |||
* @var object the sender of this event. If not set, this property will be | |||
* set as the object whose "trigger()" method is called. | |||
* set as the object whose `trigger()` method is called. | |||
* This property may also be a `null` when this event is a | |||
* class-level event which is triggered in a static context. | |||
*/ | |||
public $sender; | |||
/** | |||
* @var boolean whether the event is handled. Defaults to false. | |||
* When a handler sets this to be true, the event processing will stop and | |||
* @var boolean whether the event is handled. Defaults to `false`. | |||
* When a handler sets this to be `true`, the event processing will stop and | |||
* ignore the rest of the uninvoked event handlers. | |||
*/ | |||
public $handled = false; | |||
@@ -79,7 +79,7 @@ class Event extends Object | |||
* @param mixed $data the data to be passed to the event handler when the event is triggered. | |||
* When the event handler is invoked, this data can be accessed via [[Event::data]]. | |||
* @param boolean $append whether to append new event handler to the end of the existing | |||
* handler list. If false, the new handler will be inserted at the beginning of the existing | |||
* handler list. If `false`, the new handler will be inserted at the beginning of the existing | |||
* handler list. | |||
* @see off() | |||
*/ | |||
@@ -101,7 +101,7 @@ class Event extends Object | |||
* @param string $class the fully qualified class name from which the event handler needs to be detached. | |||
* @param string $name the event name. | |||
* @param callable $handler the event handler to be removed. | |||
* If it is null, all handlers attached to the named event will be removed. | |||
* If it is `null`, all handlers attached to the named event will be removed. | |||
* @return boolean whether a handler is found and detached. | |||
* @see on() | |||
*/ | |||
@@ -130,6 +130,17 @@ class Event extends Object | |||
} | |||
} | |||
/** | |||
* Detaches all registered class-level event handlers. | |||
* @see on() | |||
* @see off() | |||
* @since 2.0.10 | |||
*/ | |||
public static function offAll() | |||
{ | |||
self::$_events = []; | |||
} | |||
/** | |||
* Returns a value indicating whether there is any handler attached to the specified class-level event. | |||
* Note that this method will also check all parent classes to see if there is any handler attached |
@@ -964,12 +964,12 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab | |||
* Returns whether there is an element at the specified offset. | |||
* This method is required by the SPL interface [[\ArrayAccess]]. | |||
* It is implicitly called when you use something like `isset($model[$offset])`. | |||
* @param mixed $offset the offset to check on | |||
* @return boolean | |||
* @param mixed $offset the offset to check on. | |||
* @return boolean whether or not an offset exists. | |||
*/ | |||
public function offsetExists($offset) | |||
{ | |||
return $this->$offset !== null; | |||
return isset($this->$offset); | |||
} | |||
/** |
@@ -22,7 +22,7 @@ use yii\di\ServiceLocator; | |||
* accessible within the module. | |||
* | |||
* @property array $aliases List of path aliases to be defined. The array keys are alias names (must start | |||
* with '@') and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example. | |||
* with `@`) and the array values are the corresponding paths or aliases. See [[setAliases()]] for an example. | |||
* This property is write-only. | |||
* @property string $basePath The root directory of the module. | |||
* @property string $controllerPath The directory that contains the controller classes. This property is | |||
@@ -39,7 +39,7 @@ class Module extends ServiceLocator | |||
{ | |||
/** | |||
* @event ActionEvent an event raised before executing a controller action. | |||
* You may set [[ActionEvent::isValid]] to be false to cancel the action execution. | |||
* You may set [[ActionEvent::isValid]] to be `false` to cancel the action execution. | |||
*/ | |||
const EVENT_BEFORE_ACTION = 'beforeAction'; | |||
/** | |||
@@ -56,13 +56,13 @@ class Module extends ServiceLocator | |||
*/ | |||
public $id; | |||
/** | |||
* @var Module the parent module of this module. Null if this module does not have a parent. | |||
* @var Module the parent module of this module. `null` if this module does not have a parent. | |||
*/ | |||
public $module; | |||
/** | |||
* @var string|boolean the layout that should be applied for views within this module. This refers to a view name | |||
* relative to [[layoutPath]]. If this is not set, it means the layout value of the [[module|parent module]] | |||
* will be taken. If this is false, layout will be disabled within this module. | |||
* will be taken. If this is `false`, layout will be disabled within this module. | |||
*/ | |||
public $layout; | |||
/** | |||
@@ -70,7 +70,7 @@ class Module extends ServiceLocator | |||
* Each name-value pair specifies the configuration of a single controller. | |||
* A controller configuration can be either a string or an array. | |||
* If the former, the string should be the fully qualified class name of the controller. | |||
* If the latter, the array must contain a 'class' element which specifies | |||
* If the latter, the array must contain a `class` element which specifies | |||
* the controller's fully qualified class name, and the rest of the name-value pairs | |||
* in the array are used to initialize the corresponding controller properties. For example, | |||
* | |||
@@ -91,15 +91,15 @@ class Module extends ServiceLocator | |||
* class name. | |||
* | |||
* If not set, it will use the `controllers` sub-namespace under the namespace of this module. | |||
* For example, if the namespace of this module is "foo\bar", then the default | |||
* controller namespace would be "foo\bar\controllers". | |||
* For example, if the namespace of this module is `foo\bar`, then the default | |||
* controller namespace would be `foo\bar\controllers`. | |||
* | |||
* See also the [guide section on autoloading](guide:concept-autoloading) to learn more about | |||
* defining namespaces and how classes are loaded. | |||
*/ | |||
public $controllerNamespace; | |||
/** | |||
* @var string the default route of this module. Defaults to 'default'. | |||
* @var string the default route of this module. Defaults to `default`. | |||
* The route may consist of child module ID, controller ID, and/or action ID. | |||
* For example, `help`, `post/create`, `admin/post/create`. | |||
* If action ID is not given, it will take the default value as specified in | |||
@@ -127,9 +127,9 @@ class Module extends ServiceLocator | |||
/** | |||
* Constructor. | |||
* @param string $id the ID of this module | |||
* @param Module $parent the parent module (if any) | |||
* @param array $config name-value pairs that will be used to initialize the object properties | |||
* @param string $id the ID of this module. | |||
* @param Module $parent the parent module (if any). | |||
* @param array $config name-value pairs that will be used to initialize the object properties. | |||
*/ | |||
public function __construct($id, $parent = null, $config = []) | |||
{ | |||
@@ -140,9 +140,9 @@ class Module extends ServiceLocator | |||
/** | |||
* Returns the currently requested instance of this module class. | |||
* If the module class is not currently requested, null will be returned. | |||
* If the module class is not currently requested, `null` will be returned. | |||
* This method is provided so that you access the module instance from anywhere within the module. | |||
* @return static|null the currently requested instance of this module class, or null if the module class is not requested. | |||
* @return static|null the currently requested instance of this module class, or `null` if the module class is not requested. | |||
*/ | |||
public static function getInstance() | |||
{ | |||
@@ -153,7 +153,7 @@ class Module extends ServiceLocator | |||
/** | |||
* Sets the currently requested instance of this module class. | |||
* @param Module|null $instance the currently requested instance of this module class. | |||
* If it is null, the instance of the calling class will be removed, if any. | |||
* If it is `null`, the instance of the calling class will be removed, if any. | |||
*/ | |||
public static function setInstance($instance) | |||
{ | |||
@@ -252,7 +252,7 @@ class Module extends ServiceLocator | |||
/** | |||
* Sets the directory that contains the view files. | |||
* @param string $path the root directory of view files. | |||
* @throws InvalidParamException if the directory is invalid | |||
* @throws InvalidParamException if the directory is invalid. | |||
*/ | |||
public function setViewPath($path) | |||
{ | |||
@@ -287,10 +287,10 @@ class Module extends ServiceLocator | |||
* This method calls [[Yii::setAlias()]] to register the path aliases. | |||
* This method is provided so that you can define path aliases when configuring a module. | |||
* @property array list of path aliases to be defined. The array keys are alias names | |||
* (must start with '@') and the array values are the corresponding paths or aliases. | |||
* (must start with `@`) and the array values are the corresponding paths or aliases. | |||
* See [[setAliases()]] for an example. | |||
* @param array $aliases list of path aliases to be defined. The array keys are alias names | |||
* (must start with '@') and the array values are the corresponding paths or aliases. | |||
* (must start with `@`) and the array values are the corresponding paths or aliases. | |||
* For example, | |||
* | |||
* ```php | |||
@@ -332,7 +332,7 @@ class Module extends ServiceLocator | |||
* @param string $id module ID (case-sensitive). To retrieve grand child modules, | |||
* use ID path relative to this module (e.g. `admin/content`). | |||
* @param boolean $load whether to load the module if it is not yet loaded. | |||
* @return Module|null the module instance, null if the module does not exist. | |||
* @return Module|null the module instance, `null` if the module does not exist. | |||
* @see hasModule() | |||
*/ | |||
public function getModule($id, $load = true) | |||
@@ -361,14 +361,14 @@ class Module extends ServiceLocator | |||
/** | |||
* Adds a sub-module to this module. | |||
* @param string $id module ID | |||
* @param string $id module ID. | |||
* @param Module|array|null $module the sub-module to be added to this module. This can | |||
* be one of the following: | |||
* | |||
* - a [[Module]] object | |||
* - a configuration array: when [[getModule()]] is called initially, the array | |||
* will be used to instantiate the sub-module | |||
* - null: the named sub-module will be removed from this module | |||
* - `null`: the named sub-module will be removed from this module | |||
*/ | |||
public function setModule($id, $module) | |||
{ | |||
@@ -381,10 +381,10 @@ class Module extends ServiceLocator | |||
/** | |||
* Returns the sub-modules in this module. | |||
* @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set false, | |||
* @param boolean $loadedOnly whether to return the loaded sub-modules only. If this is set `false`, | |||
* then all sub-modules registered in this module will be returned, whether they are loaded or not. | |||
* Loaded modules will be returned as objects, while unloaded modules as configuration arrays. | |||
* @return array the modules (indexed by their IDs) | |||
* @return array the modules (indexed by their IDs). | |||
*/ | |||
public function getModules($loadedOnly = false) | |||
{ | |||
@@ -424,7 +424,7 @@ class Module extends ServiceLocator | |||
* ] | |||
* ``` | |||
* | |||
* @param array $modules modules (id => module configuration or instances) | |||
* @param array $modules modules (id => module configuration or instances). | |||
*/ | |||
public function setModules($modules) | |||
{ | |||
@@ -441,7 +441,7 @@ class Module extends ServiceLocator | |||
* @param string $route the route that specifies the action. | |||
* @param array $params the parameters to be passed to the action | |||
* @return mixed the result of the action. | |||
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully | |||
* @throws InvalidRouteException if the requested route cannot be resolved into an action successfully. | |||
*/ | |||
public function runAction($route, $params = []) | |||
{ | |||
@@ -452,7 +452,9 @@ class Module extends ServiceLocator | |||
$oldController = Yii::$app->controller; | |||
Yii::$app->controller = $controller; | |||
$result = $controller->runAction($actionID, $params); | |||
Yii::$app->controller = $oldController; | |||
if ($oldController !== null) { | |||
Yii::$app->controller = $oldController; | |||
} | |||
return $result; | |||
} else { | |||
@@ -476,11 +478,11 @@ class Module extends ServiceLocator | |||
* or `abc\def\XyzController` class within the [[controllerNamespace|controller namespace]]. | |||
* | |||
* If any of the above steps resolves into a controller, it is returned together with the rest | |||
* part of the route which will be treated as the action ID. Otherwise, false will be returned. | |||
* part of the route which will be treated as the action ID. Otherwise, `false` will be returned. | |||
* | |||
* @param string $route the route consisting of module, controller and action IDs. | |||
* @return array|boolean If the controller is created successfully, it will be returned together | |||
* with the requested action ID. Otherwise false will be returned. | |||
* with the requested action ID. Otherwise `false` will be returned. | |||
* @throws InvalidConfigException if the controller class and its file do not match. | |||
*/ | |||
public function createController($route) | |||
@@ -534,8 +536,8 @@ class Module extends ServiceLocator | |||
* | |||
* Note that this method does not check [[modules]] or [[controllerMap]]. | |||
* | |||
* @param string $id the controller ID | |||
* @return Controller the newly created controller instance, or null if the controller ID is invalid. | |||
* @param string $id the controller ID. | |||
* @return Controller the newly created controller instance, or `null` if the controller ID is invalid. | |||
* @throws InvalidConfigException if the controller class and its file name do not match. | |||
* This exception is only thrown when in debug mode. | |||
*/ |
@@ -143,7 +143,7 @@ class Security extends Component | |||
} | |||
/** | |||
* Verifies and decrypts data encrypted with [[encryptByPassword()]]. | |||
* Verifies and decrypts data encrypted with [[encryptByKey()]]. | |||
* @param string $data the encrypted data to decrypt | |||
* @param string $inputKey the input to use for encryption and authentication | |||
* @param string $info optional context and application specific information, see [[hkdf()]] |
@@ -140,7 +140,8 @@ class View extends Component | |||
* in the view. If the context implements [[ViewContextInterface]], it may also be used to locate | |||
* the view file corresponding to a relative view name. | |||
* @return string the rendering result | |||
* @throws InvalidParamException if the view cannot be resolved or the view file does not exist. | |||
* @throws ViewNotFoundException if the view file does not exist. | |||
* @throws InvalidCallException if the view cannot be resolved. | |||
* @see renderFile() | |||
*/ | |||
public function render($view, $params = [], $context = null) | |||
@@ -211,7 +212,7 @@ class View extends Component | |||
* @param object $context the context that the view should use for rendering the view. If null, | |||
* existing [[context]] will be used. | |||
* @return string the rendering result | |||
* @throws InvalidParamException if the view file does not exist | |||
* @throws ViewNotFoundException if the view file does not exist | |||
*/ | |||
public function renderFile($viewFile, $params = [], $context = null) | |||
{ | |||
@@ -223,7 +224,7 @@ class View extends Component | |||
if (is_file($viewFile)) { | |||
$viewFile = FileHelper::localize($viewFile); | |||
} else { | |||
throw new InvalidParamException("The view file does not exist: $viewFile"); | |||
throw new ViewNotFoundException("The view file does not exist: $viewFile"); | |||
} | |||
$oldContext = $this->context; |
@@ -0,0 +1,25 @@ | |||
<?php | |||
/** | |||
* @link http://www.yiiframework.com/ | |||
* @copyright Copyright (c) 2008 Yii Software LLC | |||
* @license http://www.yiiframework.com/license/ | |||
*/ | |||
namespace yii\base; | |||
/** | |||
* ViewNotFoundException represents an exception caused by view file not found. | |||
* | |||
* @author Alexander Makarov | |||
* @since 2.0.10 | |||
*/ | |||
class ViewNotFoundException extends InvalidParamException | |||
{ | |||
/** | |||
* @return string the user-friendly name of this exception | |||
*/ | |||
public function getName() | |||
{ | |||
return 'View not Found'; | |||
} | |||
} |
@@ -0,0 +1,340 @@ | |||
<?php | |||
/** | |||
* @link http://www.yiiframework.com/ | |||
* @copyright Copyright (c) 2008 Yii Software LLC | |||
* @license http://www.yiiframework.com/license/ | |||
*/ | |||
namespace yii\behaviors; | |||
use yii\base\Behavior; | |||
use yii\base\InvalidParamException; | |||
use yii\base\Model; | |||
use yii\db\BaseActiveRecord; | |||
use yii\validators\BooleanValidator; | |||
use yii\validators\NumberValidator; | |||
use yii\validators\StringValidator; | |||
/** | |||
* AttributeTypecastBehavior provides an ability of automatic model attribute typecasting. | |||
* This behavior is very useful in case of usage of ActiveRecord for the schema-less databases like MongoDB or Redis. | |||
* It may also come in handy for regular [[\yii\db\ActiveRecord]] or even [[\yii\base\Model]], allowing to maintain | |||
* strict attribute types after model validation. | |||
* | |||
* This behavior should be attached to [[\yii\base\Model]] or [[\yii\db\BaseActiveRecord]] descendant. | |||
* | |||
* You should specify exact attribute types via [[attributeTypes]]. | |||
* | |||
* For example: | |||
* | |||
* ```php | |||
* use yii\behaviors\AttributeTypecastBehavior; | |||
* | |||
* class Item extends \yii\db\ActiveRecord | |||
* { | |||
* public function behaviors() | |||
* { | |||
* return [ | |||
* 'typecast' => [ | |||
* 'class' => AttributeTypecastBehavior::className(), | |||
* 'attributeTypes' => [ | |||
* 'amount' => AttributeTypecastBehavior::TYPE_INTEGER, | |||
* 'price' => AttributeTypecastBehavior::TYPE_FLOAT, | |||
* 'is_active' => AttributeTypecastBehavior::TYPE_BOOLEAN, | |||
* ], | |||
* 'typecastAfterValidate' => true, | |||
* 'typecastBeforeSave' => false, | |||
* 'typecastAfterFind' => false, | |||
* ], | |||
* ]; | |||
* } | |||
* | |||
* // ... | |||
* } | |||
* ``` | |||
* | |||
* Tip: you may left [[attributeTypes]] blank - in this case its value will be detected | |||
* automatically based on owner validation rules. | |||
* Following example will automatically create same [[attributeTypes]] value as it was configured at the above one: | |||
* | |||
* ```php | |||
* use yii\behaviors\AttributeTypecastBehavior; | |||
* | |||
* class Item extends \yii\db\ActiveRecord | |||
* { | |||
* | |||
* public function rules() | |||
* { | |||
* return [ | |||
* ['amount', 'integer'], | |||
* ['price', 'number'], | |||
* ['is_active', 'boolean'], | |||
* ]; | |||
* } | |||
* | |||
* public function behaviors() | |||
* { | |||
* return [ | |||
* 'typecast' => [ | |||
* 'class' => AttributeTypecastBehavior::className(), | |||
* // 'attributeTypes' will be composed automatically according to `rules()` | |||
* ], | |||
* ]; | |||
* } | |||
* | |||
* // ... | |||
* } | |||
* ``` | |||
* | |||
* This behavior allows automatic attribute typecasting at following cases: | |||
* | |||
* - after successful model validation | |||
* - before model save (insert or update) | |||
* - after model find (found by query or refreshed) | |||
* | |||
* You may control automatic typecasting for particular case using fields [[typecastAfterValidate]], | |||
* [[typecastBeforeSave]] and [[typecastAfterFind]]. | |||
* By default typecasting will be performed only after model validation. | |||
* | |||
* Note: you can manually trigger attribute typecasting anytime invoking [[typecastAttributes()]] method: | |||
* | |||
* ```php | |||
* $model = new Item(); | |||
* $model->price = '38.5'; | |||
* $model->is_active = 1; | |||
* $model->typecastAttributes(); | |||
* ``` | |||
* | |||
* @author Paul Klimov <klimov.paul@gmail.com> | |||
* @since 2.0.10 | |||
*/ | |||
class AttributeTypecastBehavior extends Behavior | |||
{ | |||
const TYPE_INTEGER = 'integer'; | |||
const TYPE_FLOAT = 'float'; | |||
const TYPE_BOOLEAN = 'boolean'; | |||
const TYPE_STRING = 'string'; | |||
/** | |||
* @var Model|BaseActiveRecord the owner of this behavior. | |||
*/ | |||
public $owner; | |||
/** | |||
* @var array attribute typecast map in format: attributeName => type. | |||
* Type can be set via PHP callable, which accept raw value as an argument and should return | |||
* typecast result. | |||
* For example: | |||
* | |||
* ```php | |||
* [ | |||
* 'amount' => 'integer', | |||
* 'price' => 'float', | |||
* 'is_active' => 'boolean', | |||
* 'date' => function ($value) { | |||
* return ($value instanceof \DateTime) ? $value->getTimestamp(): (int)$value; | |||
* }, | |||
* ] | |||
* ``` | |||
* | |||
* If not set, attribute type map will be composed automatically from the owner validation rules. | |||
*/ | |||
public $attributeTypes; | |||
/** | |||
* @var boolean whether to skip typecasting of `null` values. | |||
* If enabled attribute value which equals to `null` will not be type-casted (e.g. `null` remains `null`), | |||
* otherwise it will be converted according to the type configured at [[attributeTypes]]. | |||
*/ | |||
public $skipOnNull = true; | |||
/** | |||
* @var boolean whether to perform typecasting after owner model validation. | |||
* Note that typecasting will be performed only if validation was successful, e.g. | |||
* owner model has no errors. | |||
* Note that changing this option value will have no effect after this behavior has been attached to the model. | |||
*/ | |||
public $typecastAfterValidate = true; | |||
/** | |||
* @var boolean whether to perform typecasting before saving owner model (insert or update). | |||
* This option may be disabled in order to achieve better performance. | |||
* For example, in case of [[\yii\db\ActiveRecord]] usage, typecasting before save | |||
* will grant no benefit an thus can be disabled. | |||
* Note that changing this option value will have no effect after this behavior has been attached to the model. | |||
*/ | |||
public $typecastBeforeSave = false; | |||
/** | |||
* @var boolean whether to perform typecasting after retrieving owner model data from | |||
* the database (after find or refresh). | |||
* This option may be disabled in order to achieve better performance. | |||
* For example, in case of [[\yii\db\ActiveRecord]] usage, typecasting after find | |||
* will grant no benefit in most cases an thus can be disabled. | |||
* Note that changing this option value will have no effect after this behavior has been attached to the model. | |||
*/ | |||
public $typecastAfterFind = false; | |||
/** | |||
* @var array internal static cache for auto detected [[attributeTypes]] values | |||
* in format: ownerClassName => attributeTypes | |||
*/ | |||
private static $autoDetectedAttributeTypes = []; | |||
/** | |||
* Clears internal static cache of auto detected [[attributeTypes]] values | |||
* over all affected owner classes. | |||
*/ | |||
public static function clearAutoDetectedAttributeTypes() | |||
{ | |||
self::$autoDetectedAttributeTypes = []; | |||
} | |||
/** | |||
* @inheritdoc | |||
*/ | |||
public function init() | |||
{ | |||
parent::init(); | |||
if ($this->attributeTypes === null) { | |||
$ownerClass = get_class($this->owner); | |||
if (!isset(self::$autoDetectedAttributeTypes[$ownerClass])) { | |||
self::$autoDetectedAttributeTypes[$ownerClass] = $this->detectAttributeTypes(); | |||
} | |||
$this->attributeTypes = self::$autoDetectedAttributeTypes[$ownerClass]; | |||
} | |||
} | |||
/** | |||
* Typecast owner attributes according to [[attributeTypes]]. | |||
* @param array $attributeNames list of attribute names that should be type-casted. | |||
* If this parameter is empty, it means any attribute listed in the [[attributeTypes]] | |||
* should be type-casted. | |||
*/ | |||
public function typecastAttributes($attributeNames = null) | |||
{ | |||
$attributeTypes = []; | |||
if ($attributeNames === null) { | |||
$attributeTypes = $this->attributeTypes; | |||
} else { | |||
foreach ($attributeNames as $attribute) { | |||
if (!isset($this->attributeTypes[$attribute])) { | |||
throw new InvalidParamException("There is no type mapping for '{$attribute}'."); | |||
} | |||
$attributeTypes[$attribute] = $this->attributeTypes[$attribute]; | |||
} | |||
} | |||
foreach ($attributeTypes as $attribute => $type) { | |||
$this->owner->{$attribute} = $this->typecastValue($this->owner->{$attribute}, $type); | |||
} | |||
} | |||
/** | |||
* Casts the given value to the specified type. | |||
* @param mixed $value value to be type-casted. | |||
* @param string|callable $type type name or typecast callable. | |||
* @return mixed typecast result. | |||
*/ | |||
protected function typecastValue($value, $type) | |||
{ | |||
if ($this->skipOnNull && $value === null) { | |||
return $value; | |||
} | |||
if (is_scalar($type)) { | |||
if (is_object($value) && method_exists($value, '__toString')) { | |||
$value = $value->__toString(); | |||
} | |||
switch ($type) { | |||
case self::TYPE_INTEGER: | |||
return (int)$value; | |||
case self::TYPE_FLOAT: | |||
return (float)$value; | |||
case self::TYPE_BOOLEAN: | |||
return (boolean)$value; | |||
case self::TYPE_STRING: | |||
return (string)$value; | |||
default: | |||
throw new InvalidParamException("Unsupported type '{$type}'"); | |||
} | |||
} | |||
return call_user_func($type, $value); | |||
} | |||
/** | |||
* Composes default value for [[attributeTypes]] from the owner validation rules. | |||
* @return array attribute type map. | |||
*/ | |||
protected function detectAttributeTypes() | |||
{ | |||
$attributeTypes = []; | |||
foreach ($this->owner->getValidators() as $validator) { | |||
$type = null; | |||
if ($validator instanceof BooleanValidator) { | |||
$type = self::TYPE_BOOLEAN; | |||
} elseif ($validator instanceof NumberValidator) { | |||
$type = $validator->integerOnly ? self::TYPE_INTEGER : self::TYPE_FLOAT; | |||
} elseif ($validator instanceof StringValidator) { | |||
$type = self::TYPE_STRING; | |||
} | |||
if ($type !== null) { | |||
foreach ((array)$validator->attributes as $attribute) { | |||
$attributeTypes[$attribute] = $type; | |||
} | |||
} | |||
} | |||
return $attributeTypes; | |||
} | |||
/** | |||
* @inheritdoc | |||
*/ | |||
public function events() | |||
{ | |||
$events = []; | |||
if ($this->typecastAfterValidate) { | |||
$events[Model::EVENT_AFTER_VALIDATE] = 'afterValidate'; | |||
} | |||
if ($this->typecastBeforeSave) { | |||
$events[BaseActiveRecord::EVENT_BEFORE_INSERT] = 'beforeSave'; | |||
$events[BaseActiveRecord::EVENT_BEFORE_UPDATE] = 'beforeSave'; | |||
} | |||
if ($this->typecastAfterFind) { | |||
$events[BaseActiveRecord::EVENT_AFTER_FIND] = 'afterFind'; | |||
} | |||
return $events; | |||
} | |||
/** | |||
* Handles owner 'afterValidate' event, ensuring attribute typecasting. | |||
* @param \yii\base\Event $event event instance. | |||
*/ | |||
public function afterValidate($event) | |||
{ | |||
if (!$this->owner->hasErrors()) { | |||
$this->typecastAttributes(); | |||
} | |||
} | |||
/** | |||
* Handles owner 'afterInsert' and 'afterUpdate' events, ensuring attribute typecasting. | |||
* @param \yii\base\Event $event event instance. | |||
*/ | |||
public function beforeSave($event) | |||
{ | |||
$this->typecastAttributes(); | |||
} | |||
/** | |||
* Handles owner 'afterFind' event, ensuring attribute typecasting. | |||
* @param \yii\base\Event $event event instance. | |||
*/ | |||
public function afterFind($event) | |||
{ | |||
$this->typecastAttributes(); | |||
} | |||
} |
@@ -67,7 +67,7 @@ class ApcCache extends Cache | |||
* 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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -90,9 +90,10 @@ class ApcCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
* @return boolean true if the value is successfully stored into cache, false otherwise. | |||
*/ | |||
protected function setValue($key, $value, $duration) | |||
{ | |||
@@ -115,7 +116,8 @@ class ApcCache extends Cache | |||
* 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ |
@@ -59,13 +59,14 @@ abstract class Cache extends Component implements \ArrayAccess | |||
*/ | |||
public $keyPrefix; | |||
/** | |||
* @var array|boolean the functions used to serialize and unserialize cached data. Defaults to null, meaning | |||
* @var null|array|false 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. [igbinary](http://pecl.php.net/package/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 [[Dependency|cache dependency]], because it relies on data serialization. | |||
* you are using [[Dependency|cache dependency]], because it relies on data serialization. Also, some | |||
* implementations of the cache can not correctly save and retrieve data different from a string type. | |||
*/ | |||
public $serializer; | |||
@@ -377,7 +378,8 @@ abstract class Cache extends Component implements \ArrayAccess | |||
* This method should be implemented by child classes to retrieve the data | |||
* from specific cache storage. | |||
* @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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. Most often | |||
* value is a string. If you have disabled [[serializer]], it could be something else. | |||
*/ | |||
abstract protected function getValue($key); | |||
@@ -386,7 +388,8 @@ abstract class Cache extends Component implements \ArrayAccess | |||
* This method should be implemented by child classes to store the data | |||
* in specific cache storage. | |||
* @param string $key the key identifying the value to be cached | |||
* @param string $value the value to be cached | |||
* @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -397,7 +400,8 @@ abstract class Cache extends Component implements \ArrayAccess | |||
* This method should be implemented by child classes to store the data | |||
* in specific cache storage. | |||
* @param string $key the key identifying the value to be cached | |||
* @param string $value the value to be cached | |||
* @param mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -460,7 +464,7 @@ abstract class Cache extends Component implements \ArrayAccess | |||
* Adds multiple key-value pairs to cache. | |||
* The default implementation calls [[addValue()]] multiple times add values one by one. If the underlying cache | |||
* storage supports multi-add, this method should be overridden to exploit that feature. | |||
* @param array $data array where key corresponds to cache key while value is the value stored | |||
* @param array $data array where key corresponds to cache key while value is the value stored. | |||
* @param integer $duration the number of seconds in which the cached values will expire. 0 means never expire. | |||
* @return array array of failed keys | |||
*/ |
@@ -119,7 +119,7 @@ class DbCache extends Cache | |||
* 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. | |||
* @return string|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -179,7 +179,7 @@ class DbCache extends 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 string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -205,7 +205,7 @@ class DbCache extends 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 string $value the value to be cached. Other types (if you have disabled [[serializer]]) cannot be saved. | |||
* @param integer $duration 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 | |||
*/ |
@@ -24,7 +24,7 @@ class DummyCache extends Cache | |||
* 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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -36,7 +36,7 @@ class DummyCache extends 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 mixed $value the value to be cached | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -49,7 +49,7 @@ class DummyCache extends Cache | |||
* 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 mixed $value the value to be cached | |||
* @param integer $duration 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 | |||
*/ |
@@ -102,7 +102,7 @@ class FileCache extends Cache | |||
* 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. | |||
* @return string|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -127,7 +127,8 @@ class FileCache extends 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 string $value the value to be cached. Other types (If you have disabled [[serializer]]) unable to get is | |||
* correct in [[getValue()]]. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -159,7 +160,8 @@ class FileCache extends 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 string $value the value to be cached. Other types (if you have disabled [[serializer]]) unable to get is | |||
* correct in [[getValue()]]. | |||
* @param integer $duration 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 | |||
*/ |
@@ -261,7 +261,7 @@ class MemCache extends Cache | |||
* 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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -283,13 +283,16 @@ class MemCache extends 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 mixed $value the value to be cached. | |||
* @see \MemcachePool::set() | |||
* @param integer $duration 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, $duration) | |||
{ | |||
$duration = $this->trimDuration($duration); | |||
// Use UNIX timestamp since it doesn't have any limitation | |||
// @see http://php.net/manual/en/memcache.set.php | |||
// @see http://php.net/manual/en/memcached.expiration.php | |||
$expire = $duration > 0 ? $duration + time() : 0; | |||
return $this->useMemcached ? $this->_cache->set($key, $value, $expire) : $this->_cache->set($key, $value, 0, $expire); | |||
@@ -303,10 +306,12 @@ class MemCache extends Cache | |||
*/ | |||
protected function setValues($data, $duration) | |||
{ | |||
$duration = $this->trimDuration($duration); | |||
if ($this->useMemcached) { | |||
$this->_cache->setMulti($data, $duration > 0 ? $duration + time() : 0); | |||
// Use UNIX timestamp since it doesn't have any limitation | |||
// @see http://php.net/manual/en/memcache.set.php | |||
// @see http://php.net/manual/en/memcached.expiration.php | |||
$expire = $duration > 0 ? $duration + time() : 0; | |||
$this->_cache->setMulti($data, $expire); | |||
return []; | |||
} else { | |||
@@ -319,13 +324,16 @@ class MemCache extends 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 mixed $value the value to be cached | |||
* @see \MemcachePool::set() | |||
* @param integer $duration 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, $duration) | |||
{ | |||
$duration = $this->trimDuration($duration); | |||
// Use UNIX timestamp since it doesn't have any limitation | |||
// @see http://php.net/manual/en/memcache.set.php | |||
// @see http://php.net/manual/en/memcached.expiration.php | |||
$expire = $duration > 0 ? $duration + time() : 0; | |||
return $this->useMemcached ? $this->_cache->add($key, $value, $expire) : $this->_cache->add($key, $value, 0, $expire); | |||
@@ -351,21 +359,4 @@ class MemCache extends Cache | |||
{ | |||
return $this->_cache->flush(); | |||
} | |||
/** | |||
* Trims duration to 30 days (2592000 seconds). | |||
* @param integer $duration the number of seconds | |||
* @return integer the duration | |||
* @since 2.0.7 | |||
* @see http://php.net/manual/en/memcache.set.php | |||
* @see http://php.net/manual/en/memcached.expiration.php | |||
*/ | |||
protected function trimDuration($duration) | |||
{ | |||
if ($duration > 2592000) { | |||
Yii::warning('Duration has been truncated to 30 days due to Memcache/Memcached limitation.', __METHOD__); | |||
return 2592000; | |||
} | |||
return $duration; | |||
} | |||
} |
@@ -63,7 +63,8 @@ class WinCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -88,7 +89,8 @@ class WinCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ |
@@ -42,7 +42,7 @@ class XCache extends Cache | |||
* 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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -54,7 +54,8 @@ class XCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -68,7 +69,8 @@ class XCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ |
@@ -24,7 +24,7 @@ class ZendDataCache extends Cache | |||
* 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. | |||
* @return mixed|false the value stored in cache, false if the value is not in the cache or expired. | |||
*/ | |||
protected function getValue($key) | |||
{ | |||
@@ -38,7 +38,8 @@ class ZendDataCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ | |||
@@ -52,7 +53,8 @@ class ZendDataCache extends 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 mixed $value the value to be cached. Most often it's a string. If you have disabled [[serializer]], | |||
* it could be something else. | |||
* @param integer $duration 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 | |||
*/ |
@@ -17,8 +17,8 @@ use yii\widgets\InputWidget; | |||
/** | |||
* Captcha renders a CAPTCHA image and an input field that takes user-entered verification code. | |||
* | |||
* Captcha is used together with [[CaptchaAction]] provide [CAPTCHA](http://en.wikipedia.org/wiki/Captcha) - a way | |||
* of preventing Website spamming. | |||
* Captcha is used together with [[CaptchaAction]] to provide [CAPTCHA](http://en.wikipedia.org/wiki/Captcha) - a way | |||
* of preventing website spamming. | |||
* | |||
* The image element rendered by Captcha will display a CAPTCHA image generated by | |||
* an action whose route is specified by [[captchaAction]]. This action must be an instance of [[CaptchaAction]]. |
@@ -142,7 +142,7 @@ class CaptchaAction extends Action | |||
} | |||
/** | |||
* Generates a hash code that can be used for client side validation. | |||
* Generates a hash code that can be used for client-side validation. | |||
* @param string $code the CAPTCHA code | |||
* @return string a hash code generated from the CAPTCHA code | |||
*/ |
@@ -51,9 +51,11 @@ return [ | |||
'yii\base\View' => YII2_PATH . '/base/View.php', | |||
'yii\base\ViewContextInterface' => YII2_PATH . '/base/ViewContextInterface.php', | |||
'yii\base\ViewEvent' => YII2_PATH . '/base/ViewEvent.php', | |||
'yii\base\ViewNotFoundException' => YII2_PATH . '/base/ViewNotFoundException.php', | |||
'yii\base\ViewRenderer' => YII2_PATH . '/base/ViewRenderer.php', | |||
'yii\base\Widget' => YII2_PATH . '/base/Widget.php', | |||
'yii\behaviors\AttributeBehavior' => YII2_PATH . '/behaviors/AttributeBehavior.php', | |||
'yii\behaviors\AttributeTypecastBehavior' => YII2_PATH . '/behaviors/AttributeTypecastBehavior.php', | |||
'yii\behaviors\BlameableBehavior' => YII2_PATH . '/behaviors/BlameableBehavior.php', | |||
'yii\behaviors\SluggableBehavior' => YII2_PATH . '/behaviors/SluggableBehavior.php', | |||
'yii\behaviors\TimestampBehavior' => YII2_PATH . '/behaviors/TimestampBehavior.php', | |||
@@ -179,7 +181,9 @@ return [ | |||
'yii\helpers\Inflector' => YII2_PATH . '/helpers/Inflector.php', | |||
'yii\helpers\Json' => YII2_PATH . '/helpers/Json.php', | |||
'yii\helpers\Markdown' => YII2_PATH . '/helpers/Markdown.php', | |||
'yii\helpers\ReplaceArrayValue' => YII2_PATH . '/helpers/ReplaceArrayValue.php', | |||
'yii\helpers\StringHelper' => YII2_PATH . '/helpers/StringHelper.php', | |||
'yii\helpers\UnsetArrayValue' => YII2_PATH . '/helpers/UnsetArrayValue.php', | |||
'yii\helpers\Url' => YII2_PATH . '/helpers/Url.php', | |||
'yii\helpers\VarDumper' => YII2_PATH . '/helpers/VarDumper.php', | |||
'yii\i18n\DbMessageSource' => YII2_PATH . '/i18n/DbMessageSource.php', | |||
@@ -209,6 +213,7 @@ return [ | |||
'yii\mutex\FileMutex' => YII2_PATH . '/mutex/FileMutex.php', | |||
'yii\mutex\Mutex' => YII2_PATH . '/mutex/Mutex.php', | |||
'yii\mutex\MysqlMutex' => YII2_PATH . '/mutex/MysqlMutex.php', | |||
'yii\mutex\OracleMutex' => YII2_PATH . '/mutex/OracleMutex.php', | |||
'yii\mutex\PgsqlMutex' => YII2_PATH . '/mutex/PgsqlMutex.php', | |||
'yii\rbac\Assignment' => YII2_PATH . '/rbac/Assignment.php', | |||
'yii\rbac\BaseManager' => YII2_PATH . '/rbac/BaseManager.php', | |||
@@ -291,6 +296,7 @@ return [ | |||
'yii\web\Linkable' => YII2_PATH . '/web/Linkable.php', | |||
'yii\web\MethodNotAllowedHttpException' => YII2_PATH . '/web/MethodNotAllowedHttpException.php', | |||
'yii\web\MultiFieldSession' => YII2_PATH . '/web/MultiFieldSession.php', | |||
'yii\web\MultipartFormDataParser' => YII2_PATH . '/web/MultipartFormDataParser.php', | |||
'yii\web\NotAcceptableHttpException' => YII2_PATH . '/web/NotAcceptableHttpException.php', | |||
'yii\web\NotFoundHttpException' => YII2_PATH . '/web/NotFoundHttpException.php', | |||
'yii\web\Request' => YII2_PATH . '/web/Request.php', | |||
@@ -306,6 +312,8 @@ return [ | |||
'yii\web\UnsupportedMediaTypeHttpException' => YII2_PATH . '/web/UnsupportedMediaTypeHttpException.php', | |||
'yii\web\UploadedFile' => YII2_PATH . '/web/UploadedFile.php', | |||
'yii\web\UrlManager' => YII2_PATH . '/web/UrlManager.php', | |||
'yii\web\UrlNormalizer' => YII2_PATH . '/web/UrlNormalizer.php', | |||
'yii\web\UrlNormalizerRedirectException' => YII2_PATH . '/web/UrlNormalizerRedirectException.php', | |||
'yii\web\UrlRule' => YII2_PATH . '/web/UrlRule.php', | |||
'yii\web\UrlRuleInterface' => YII2_PATH . '/web/UrlRuleInterface.php', | |||
'yii\web\User' => YII2_PATH . '/web/User.php', |
@@ -51,6 +51,11 @@ class Controller extends \yii\base\Controller | |||
* If not set, ANSI color will only be enabled for terminals that support it. | |||
*/ | |||
public $color; | |||
/** | |||
* @var boolean whether to display help information about current command. | |||
* @since 2.0.10 | |||
*/ | |||
public $help; | |||
/** | |||
* @var array the options passed during execution. | |||
@@ -102,7 +107,7 @@ class Controller extends \yii\base\Controller | |||
if (in_array($name, $options, true)) { | |||
$default = $this->$name; | |||
if (is_array($default)) { | |||
$this->$name = preg_split('/(?!\(\d+)\s*,\s*(?!\d+\))/', $value); | |||
$this->$name = preg_split('/\s*,\s*(?![^()]*\))/', $value); | |||
} elseif ($default !== null) { | |||
settype($value, gettype($default)); | |||
$this->$name = $value; | |||
@@ -116,6 +121,10 @@ class Controller extends \yii\base\Controller | |||
} | |||
} | |||
} | |||
if ($this->help) { | |||
$route = $this->id . '/' . $id; | |||
return Yii::$app->runAction('help', [$route]); | |||
} | |||
return parent::runAction($id, $params); | |||
} | |||
@@ -246,6 +255,19 @@ class Controller extends \yii\base\Controller | |||
* - validator: a callable function to validate input. The function must accept two parameters: | |||
* - $input: the user input to validate | |||
* - $error: the error value passed by reference if validation failed. | |||
* | |||
* An example of how to use the prompt method with a validator function. | |||
* | |||
* ```php | |||
* $code = $this->prompt('Enter 4-Chars-Pin', ['required' => true, 'validator' => function($input, &$error) { | |||
* if (strlen($input) !== 4) { | |||
* $error = 'The Pin must be exactly 4 chars!'; | |||
* return false; | |||
* } | |||
* return true; | |||
* }); | |||
* ``` | |||
* | |||
* @return string the user input | |||
*/ | |||
public function prompt($text, $options = []) | |||
@@ -303,7 +325,7 @@ class Controller extends \yii\base\Controller | |||
public function options($actionID) | |||
{ | |||
// $actionId might be used in subclasses to provide options specific to action id | |||
return ['color', 'interactive']; | |||
return ['color', 'interactive', 'help']; | |||
} | |||
/** | |||
@@ -318,7 +340,9 @@ class Controller extends \yii\base\Controller | |||
*/ | |||
public function optionAliases() | |||
{ | |||
return []; | |||
return [ | |||
'h' => 'help' | |||
]; | |||
} | |||
/** |
@@ -41,6 +41,7 @@ use yii\web\AssetBundle; | |||
* differs in getter and setter. See [[getAssetManager()]] and [[setAssetManager()]] for details. | |||
* | |||
* @author Qiang Xue <qiang.xue@gmail.com> | |||
* @author Paul Klimov <klimov.paul@gmail.com> | |||
* @since 2.0 | |||
*/ | |||
class AssetController extends Controller | |||
@@ -120,6 +121,12 @@ class AssetController extends Controller | |||
* @see https://github.com/yui/yuicompressor/ | |||
*/ | |||
public $cssCompressor = 'java -jar yuicompressor.jar --type css {from} -o {to}'; | |||
/** | |||
* @var boolean whether to delete asset source files after compression. | |||
* This option affects only those bundles, which have [[\yii\web\AssetBundle::sourcePath]] is set. | |||
* @since 2.0.10 | |||
*/ | |||
public $deleteSource = false; | |||
/** | |||
* @var array|\yii\web\AssetManager [[\yii\web\AssetManager]] instance or its array configuration, which will be used | |||
@@ -146,6 +153,11 @@ class AssetController extends Controller | |||
if (!isset($options['baseUrl'])) { | |||
throw new Exception("Please specify 'baseUrl' for the 'assetManager' option."); | |||
} | |||
if (!isset($options['forceCopy'])) { | |||
$options['forceCopy'] = true; | |||
} | |||
$this->_assetManager = Yii::createObject($options); | |||
} | |||
@@ -190,6 +202,10 @@ class AssetController extends Controller | |||
$targets = $this->adjustDependency($targets, $bundles); | |||
$this->saveTargets($targets, $bundleFile); | |||
if ($this->deleteSource) { | |||
$this->deletePublishedAssets($bundles); | |||
} | |||
} | |||
/** | |||
@@ -402,10 +418,9 @@ class AssetController extends Controller | |||
if (!$this->isBundleExternal($sourceBundle)) { | |||
$depends[] = $target; | |||
} | |||
$targets[$bundle] = Yii::createObject([ | |||
'class' => strpos($bundle, '\\') !== false ? $bundle : 'yii\\web\\AssetBundle', | |||
'depends' => $depends, | |||
]); | |||
$targetBundle = clone $sourceBundle; | |||
$targetBundle->depends = $depends; | |||
$targets[$bundle] = $targetBundle; | |||
} | |||
return $targets; | |||
@@ -444,13 +459,15 @@ class AssetController extends Controller | |||
$array = []; | |||
foreach ($targets as $name => $target) { | |||
if (isset($this->targets[$name])) { | |||
$array[$name] = [ | |||
$array[$name] = array_merge($this->targets[$name], [ | |||
'class' => get_class($target), | |||
'sourcePath' => null, | |||
'basePath' => $this->targets[$name]['basePath'], | |||
'baseUrl' => $this->targets[$name]['baseUrl'], | |||
'js' => $target->js, | |||
'css' => $target->css, | |||
]; | |||
'depends' => [], | |||
]); | |||
} else { | |||
if ($this->isBundleExternal($target)) { | |||
$array[$name] = $this->composeBundleConfig($target); | |||
@@ -683,6 +700,8 @@ return [ | |||
'jsCompressor' => {$jsCompressor}, | |||
// Adjust command/callback for CSS files compressing: | |||
'cssCompressor' => {$cssCompressor}, | |||
// Whether to delete asset source after compression: | |||
'deleteSource' => false, | |||
// The list of asset bundles to compress: | |||
'bundles' => [ | |||
// 'app\assets\AppAsset', | |||
@@ -782,4 +801,32 @@ EOD; | |||
$dependencyTrace[] = $circularDependencyName; | |||
return implode(' -> ', $dependencyTrace); | |||
} | |||
/** | |||
* Deletes bundle asset files, which have been published from `sourcePath`. | |||
* @param \yii\web\AssetBundle[] $bundles asset bundles to be processed. | |||
* @since 2.0.10 | |||
*/ | |||
private function deletePublishedAssets($bundles) | |||
{ | |||
$this->stdout("Deleting source files...\n"); | |||
if ($this->getAssetManager()->linkAssets) { | |||
$this->stdout("`AssetManager::linkAssets` option is enabled. Deleting of source files canceled.\n", Console::FG_YELLOW); | |||
return; | |||
} | |||
foreach ($bundles as $bundle) { | |||
if ($bundle->sourcePath !== null) { | |||
foreach ($bundle->js as $jsFile) { | |||
@unlink($bundle->basePath . DIRECTORY_SEPARATOR . $jsFile); | |||
} | |||
foreach ($bundle->css as $cssFile) { | |||
@unlink($bundle->basePath . DIRECTORY_SEPARATOR . $cssFile); | |||
} | |||
} | |||
} | |||
$this->stdout("Source files deleted.\n", Console::FG_GREEN); | |||
} | |||
} |
@@ -8,13 +8,14 @@ | |||
namespace yii\console\controllers; | |||
use Yii; | |||
use yii\base\InvalidConfigException; | |||
use yii\console\Exception; | |||
use yii\console\Controller; | |||
use yii\helpers\Console; | |||
use yii\helpers\FileHelper; | |||
/** | |||
* BaseMigrateController is base class for migrate controllers. | |||
* BaseMigrateController is the base class for migrate controllers. | |||
* | |||
* @author Qiang Xue <qiang.xue@gmail.com> | |||
* @since 2.0 | |||
@@ -31,10 +32,32 @@ abstract class BaseMigrateController extends Controller | |||
*/ | |||
public $defaultAction = 'up'; | |||
/** | |||
* @var string the directory storing the migration classes. This can be either | |||
* a path alias or a directory. | |||
* @var string the directory containing the migration classes. This can be either | |||
* a path alias or a directory path. | |||
* | |||
* If you have set up [[migrationNamespaces]], you may set this field to `null` in order | |||
* to disable usage of migrations that are not namespaced. | |||
*/ | |||
public $migrationPath = '@app/migrations'; | |||
/** | |||
* @var array list of namespaces containing the migration classes. | |||
* | |||
* Migration namespaces should be resolvable as a path alias if prefixed with `@`, e.g. if you specify | |||
* the namespace `app\migrations`, the code `Yii::getAlias('@app/migrations')` should be able to return | |||
* the file path to the directory this namespace refers to. | |||
* | |||
* For example: | |||
* | |||
* ```php | |||
* [ | |||
* 'app\migrations', | |||
* 'some\extension\migrations', | |||
* ] | |||
* ``` | |||
* | |||
* @since 2.0.10 | |||
*/ | |||
public $migrationNamespaces = []; | |||
/** | |||
* @var string the template file for generating new migrations. | |||
* This can be either a path alias (e.g. "@app/migrations/template.php") | |||
@@ -59,20 +82,30 @@ abstract class BaseMigrateController extends Controller | |||
* This method is invoked right before an action is to be executed (after all possible filters.) | |||
* It checks the existence of the [[migrationPath]]. | |||
* @param \yii\base\Action $action the action to be executed. | |||
* @throws Exception if directory specified in migrationPath doesn't exist and action isn't "create". | |||
* @throws InvalidConfigException if directory specified in migrationPath doesn't exist and action isn't "create". | |||
* @return boolean whether the action should continue to be executed. | |||
*/ | |||
public function beforeAction($action) | |||
{ | |||
if (parent::beforeAction($action)) { | |||
$path = Yii::getAlias($this->migrationPath); | |||
if (!is_dir($path)) { | |||
if ($action->id !== 'create') { | |||
throw new Exception("Migration failed. Directory specified in migrationPath doesn't exist: {$this->migrationPath}"); | |||
if (empty($this->migrationNamespaces) && empty($this->migrationPath)) { | |||
throw new InvalidConfigException('At least one of `migrationPath` or `migrationNamespaces` should be specified.'); | |||
} | |||
foreach ($this->migrationNamespaces as $key => $value) { | |||
$this->migrationNamespaces[$key] = trim($value, '\\'); | |||
} | |||
if ($this->migrationPath !== null) { | |||
$path = Yii::getAlias($this->migrationPath); | |||
if (!is_dir($path)) { | |||
if ($action->id !== 'create') { | |||
throw new InvalidConfigException("Migration failed. Directory specified in migrationPath doesn't exist: {$this->migrationPath}"); | |||
} | |||
FileHelper::createDirectory($path); | |||
} | |||
FileHelper::createDirectory($path); | |||
$this->migrationPath = $path; | |||
} | |||
$this->migrationPath = $path; | |||
$version = Yii::getVersion(); | |||
$this->stdout("Yii Migration Tool (based on Yii v{$version})\n\n"); | |||
@@ -257,7 +290,7 @@ abstract class BaseMigrateController extends Controller | |||
} | |||
foreach (array_reverse($migrations) as $migration) { | |||
if (!$this->migrateUp($migration)) { | |||
$this->stdout("\nMigration failed. The rest of the migrations migrations are canceled.\n", Console::FG_RED); | |||
$this->stdout("\nMigration failed. The rest of the migrations are canceled.\n", Console::FG_RED); | |||
return self::EXIT_CODE_ERROR; | |||
} | |||
@@ -278,10 +311,11 @@ abstract class BaseMigrateController extends Controller | |||
* them again. For example, | |||
* | |||
* ``` | |||
* yii migrate/to 101129_185401 # using timestamp | |||
* yii migrate/to m101129_185401_create_user_table # using full name | |||
* yii migrate/to 1392853618 # using UNIX timestamp | |||
* yii migrate/to "2014-02-15 13:00:50" # using strtotime() parseable string | |||
* yii migrate/to 101129_185401 # using timestamp | |||
* yii migrate/to m101129_185401_create_user_table # using full name | |||
* yii migrate/to 1392853618 # using UNIX timestamp | |||
* yii migrate/to "2014-02-15 13:00:50" # using strtotime() parseable string | |||
* yii migrate/to app\migrations\M101129185401CreateUser # using full namespace name | |||
* ``` | |||
* | |||
* @param string $version either the version name or the certain time value in the past | |||
@@ -292,14 +326,16 @@ abstract class BaseMigrateController extends Controller | |||
*/ | |||
public function actionTo($version) | |||
{ | |||
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) { | |||
$this->migrateToVersion('m' . $matches[1]); | |||
if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) { | |||
$this->migrateToVersion($namespaceVersion); | |||
} elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) { | |||
$this->migrateToVersion($migrationName); | |||
} elseif ((string) (int) $version == $version) { | |||
$this->migrateToTime($version); | |||
} elseif (($time = strtotime($version)) !== false) { | |||
$this->migrateToTime($time); | |||
} else { | |||
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401),\n the full name of a migration (e.g. m101129_185401_create_user_table),\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\nby the strtotime() function (e.g. 2014-02-15 13:00:50)."); | |||
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401),\n the full name of a migration (e.g. m101129_185401_create_user_table),\n the full namespaced name of a migration (e.g. app\\migrations\\M101129185401CreateUserTable),\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\nby the strtotime() function (e.g. 2014-02-15 13:00:50)."); | |||
} | |||
} | |||
@@ -309,8 +345,9 @@ abstract class BaseMigrateController extends Controller | |||
* No actual migration will be performed. | |||
* | |||
* ``` | |||
* yii migrate/mark 101129_185401 # using timestamp | |||
* yii migrate/mark m101129_185401_create_user_table # using full name | |||
* yii migrate/mark 101129_185401 # using timestamp | |||
* yii migrate/mark m101129_185401_create_user_table # using full name | |||
* yii migrate/to app\migrations\M101129185401CreateUser # using full namespace name | |||
* ``` | |||
* | |||
* @param string $version the version at which the migration history should be marked. | |||
@@ -321,16 +358,18 @@ abstract class BaseMigrateController extends Controller | |||
public function actionMark($version) | |||
{ | |||
$originalVersion = $version; | |||
if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) { | |||
$version = 'm' . $matches[1]; | |||
if (($namespaceVersion = $this->extractNamespaceMigrationVersion($version)) !== false) { | |||
$version = $namespaceVersion; | |||
} elseif (($migrationName = $this->extractMigrationVersion($version)) !== false) { | |||
$version = $migrationName; | |||
} else { | |||
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table)."); | |||
throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table)\nor the full name of a namespaced migration (e.g. app\\migrations\\M101129185401CreateUserTable)."); | |||
} | |||
// try mark up | |||
$migrations = $this->getNewMigrations(); | |||
foreach ($migrations as $i => $migration) { | |||
if (strpos($migration, $version . '_') === 0) { | |||
if (strpos($migration, $version) === 0) { | |||
if ($this->confirm("Set migration history at $originalVersion?")) { | |||
for ($j = 0; $j <= $i; ++$j) { | |||
$this->addMigrationHistory($migrations[$j]); | |||
@@ -345,7 +384,7 @@ abstract class BaseMigrateController extends Controller | |||
// try mark down | |||
$migrations = array_keys($this->getMigrationHistory(null)); | |||
foreach ($migrations as $i => $migration) { | |||
if (strpos($migration, $version . '_') === 0) { | |||
if (strpos($migration, $version) === 0) { | |||
if ($i === 0) { | |||
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW); | |||
} else { | |||
@@ -364,6 +403,34 @@ abstract class BaseMigrateController extends Controller | |||
throw new Exception("Unable to find the version '$originalVersion'."); | |||
} | |||
/** | |||
* Checks if given migration version specification matches namespaced migration name. | |||
* @param string $rawVersion raw version specification received from user input. | |||
* @return string|false actual migration version, `false` - if not match. | |||
* @since 2.0.10 | |||
*/ | |||
private function extractNamespaceMigrationVersion($rawVersion) | |||
{ | |||
if (preg_match('/^\\\\?([\w_]+\\\\)+m(\d{6}_?\d{6})(\D.*)?$/is', $rawVersion, $matches)) { | |||
return trim($rawVersion, '\\'); | |||
} | |||
return false; | |||
} | |||
/** | |||
* Checks if given migration version specification matches migration base name. | |||
* @param string $rawVersion raw version specification received from user input. | |||
* @return string|false actual migration version, `false` - if not match. | |||
* @since 2.0.10 | |||
*/ | |||
private function extractMigrationVersion($rawVersion) | |||
{ | |||
if (preg_match('/^m?(\d{6}_?\d{6})(\D.*)?$/is', $rawVersion, $matches)) { | |||
return 'm' . $matches[1]; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Displays the migration history. | |||
* | |||
@@ -465,33 +532,108 @@ abstract class BaseMigrateController extends Controller | |||
* yii migrate/create create_user_table | |||
* ``` | |||
* | |||
* In order to generate a namespaced migration, you should specify a namespace before the migration's name. | |||
* Note that backslash (`\`) is usually considered a special character in the shell, so you need to escape it | |||
* properly to avoid shell errors or incorrect behavior. | |||
* For example: | |||
* | |||
* ``` | |||
* yii migrate/create 'app\\migrations\\createUserTable' | |||
* ``` | |||
* | |||
* In case [[migrationPath]] is not set and no namespace is provided, the first entry of [[migrationNamespaces]] will be used. | |||
* | |||
* @param string $name the name of the new migration. This should only contain | |||
* letters, digits and/or underscores. | |||
* letters, digits, underscores and/or backslashes. | |||
* | |||
* Note: If the migration name is of a special form, for example create_xxx or | |||
* drop_xxx then the generated migration file will contain extra code, | |||
* drop_xxx, then the generated migration file will contain extra code, | |||
* in this case for creating/dropping tables. | |||
* | |||
* @throws Exception if the name argument is invalid. | |||
*/ | |||
public function actionCreate($name) | |||
{ | |||
if (!preg_match('/^\w+$/', $name)) { | |||
throw new Exception('The migration name should contain letters, digits and/or underscore characters only.'); | |||
if (!preg_match('/^[\w\\\\]+$/', $name)) { | |||
throw new Exception('The migration name should contain letters, digits, underscore and/or backslash characters only.'); | |||
} | |||
$className = 'm' . gmdate('ymd_His') . '_' . $name; | |||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $className . '.php'; | |||
list($namespace, $className) = $this->generateClassName($name); | |||
$migrationPath = $this->findMigrationPath($namespace); | |||
$file = $migrationPath . DIRECTORY_SEPARATOR . $className . '.php'; | |||
if ($this->confirm("Create new migration '$file'?")) { | |||
$content = $this->generateMigrationSourceCode([ | |||
'name' => $name, | |||
'className' => $className, | |||
'namespace' => $namespace, | |||
]); | |||
FileHelper::createDirectory($migrationPath); | |||
file_put_contents($file, $content); | |||
$this->stdout("New migration created successfully.\n", Console::FG_GREEN); | |||
} | |||
} | |||
/** | |||
* Generates class base name and namespace from migration name from user input. | |||
* @param string $name migration name from user input. | |||
* @return array list of 2 elements: 'namespace' and 'class base name' | |||
* @since 2.0.10 | |||
*/ | |||
private function generateClassName($name) | |||
{ | |||
$namespace = null; | |||
$name = trim($name, '\\'); | |||
if (strpos($name, '\\') !== false) { | |||
$namespace = substr($name, 0, strrpos($name, '\\')); | |||
$name = substr($name, strrpos($name, '\\') + 1); | |||
} else { | |||
if ($this->migrationPath === null) { | |||
$migrationNamespaces = $this->migrationNamespaces; | |||
$namespace = array_shift($migrationNamespaces); | |||
} | |||
} | |||
if ($namespace === null) { | |||
$class = 'm' . gmdate('ymd_His') . '_' . $name; | |||
} else { | |||
$class = 'M' . gmdate('ymdHis') . ucfirst($name); | |||
} | |||
return [$namespace, $class]; | |||
} | |||
/** | |||
* Finds the file path for the specified migration namespace. | |||
* @param string|null $namespace migration namespace. | |||
* @return string migration file path. | |||
* @throws Exception on failure. | |||
* @since 2.0.10 | |||
*/ | |||
private function findMigrationPath($namespace) | |||
{ | |||
if (empty($namespace)) { | |||
return $this->migrationPath; | |||
} | |||
if (!in_array($namespace, $this->migrationNamespaces, true)) { | |||
throw new Exception("Namespace '{$namespace}' not found in `migrationNamespaces`"); | |||
} | |||
return $this->getNamespacePath($namespace); | |||
} | |||
/** | |||
* Returns the file path matching the give namespace. | |||
* @param string $namespace namespace. | |||
* @return string file path. | |||
* @since 2.0.10 | |||
*/ | |||
private function getNamespacePath($namespace) | |||
{ | |||
return str_replace('/', DIRECTORY_SEPARATOR, Yii::getAlias('@' . str_replace('\\', '/', $namespace))); | |||
} | |||
/** | |||
* Upgrades with the specified migration class. | |||
* @param string $class the migration class name | |||
@@ -539,7 +681,6 @@ abstract class BaseMigrateController extends Controller | |||
$time = microtime(true) - $start; | |||
$this->stdout("*** reverted $class (time: " . sprintf('%.3f', $time) . "s)\n\n", Console::FG_GREEN); | |||
return true; | |||
} else { | |||
$time = microtime(true) - $start; | |||
@@ -556,8 +697,11 @@ abstract class BaseMigrateController extends Controller | |||
*/ | |||
protected function createMigration($class) | |||
{ | |||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php'; | |||
require_once($file); | |||
$class = trim($class, '\\'); | |||
if (strpos($class, '\\') === false) { | |||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php'; | |||
require_once($file); | |||
} | |||
return new $class(); | |||
} | |||
@@ -593,7 +737,7 @@ abstract class BaseMigrateController extends Controller | |||
// try migrate up | |||
$migrations = $this->getNewMigrations(); | |||
foreach ($migrations as $i => $migration) { | |||
if (strpos($migration, $version . '_') === 0) { | |||
if (strpos($migration, $version) === 0) { | |||
$this->actionUp($i + 1); | |||
return self::EXIT_CODE_NORMAL; | |||
@@ -603,7 +747,7 @@ abstract class BaseMigrateController extends Controller | |||
// try migrate down | |||
$migrations = array_keys($this->getMigrationHistory(null)); | |||
foreach ($migrations as $i => $migration) { | |||
if (strpos($migration, $version . '_') === 0) { | |||
if (strpos($migration, $version) === 0) { | |||
if ($i === 0) { | |||
$this->stdout("Already at '$originalVersion'. Nothing needs to be done.\n", Console::FG_YELLOW); | |||
} else { | |||
@@ -624,25 +768,45 @@ abstract class BaseMigrateController extends Controller | |||
protected function getNewMigrations() | |||
{ | |||
$applied = []; | |||
foreach ($this->getMigrationHistory(null) as $version => $time) { | |||
$applied[substr($version, 1, 13)] = true; | |||
foreach ($this->getMigrationHistory(null) as $class => $time) { | |||
$applied[trim($class, '\\')] = true; | |||
} | |||
$migrationPaths = []; | |||
if (!empty($this->migrationPath)) { | |||
$migrationPaths[''] = $this->migrationPath; | |||
} | |||
foreach ($this->migrationNamespaces as $namespace) { | |||
$migrationPaths[$namespace] = $this->getNamespacePath($namespace); | |||
} | |||
$migrations = []; | |||
$handle = opendir($this->migrationPath); | |||
while (($file = readdir($handle)) !== false) { | |||
if ($file === '.' || $file === '..') { | |||
foreach ($migrationPaths as $namespace => $migrationPath) { | |||
if (!file_exists($migrationPath)) { | |||
continue; | |||
} | |||
$path = $this->migrationPath . DIRECTORY_SEPARATOR . $file; | |||
if (preg_match('/^(m(\d{6}_\d{6})_.*?)\.php$/', $file, $matches) && !isset($applied[$matches[2]]) && is_file($path)) { | |||
$migrations[] = $matches[1]; | |||
$handle = opendir($migrationPath); | |||
while (($file = readdir($handle)) !== false) { | |||
if ($file === '.' || $file === '..') { | |||
continue; | |||
} | |||
$path = $migrationPath . DIRECTORY_SEPARATOR . $file; | |||
if (preg_match('/^(m(\d{6}_?\d{6})\D.*?)\.php$/is', $file, $matches) && is_file($path)) { | |||
$class = $matches[1]; | |||
if (!empty($namespace)) { | |||
$class = $namespace . '\\' . $class; | |||
} | |||
$time = str_replace('_', '', $matches[2]); | |||
if (!isset($applied[$class])) { | |||
$migrations[$time . '\\' . $class] = $class; | |||
} | |||
} | |||
} | |||
closedir($handle); | |||
} | |||
closedir($handle); | |||
sort($migrations); | |||
ksort($migrations); | |||
return $migrations; | |||
return array_values($migrations); | |||
} | |||
/** |
@@ -98,6 +98,8 @@ class FixtureController extends Controller | |||
* yii fixture/load "*, -User, -UserProfile" | |||
* ``` | |||
* | |||
* @param array $fixturesInput | |||
* @return int return code | |||
* @throws Exception if the specified fixture does not exist. | |||
*/ | |||
public function actionLoad(array $fixturesInput = []) | |||
@@ -175,6 +177,8 @@ class FixtureController extends Controller | |||
* yii fixture/unload "*, -User, -UserProfile" | |||
* ``` | |||
* | |||
* @param array $fixturesInput | |||
* @return int return code | |||
* @throws Exception if the specified fixture does not exist. | |||
*/ | |||
public function actionUnload(array $fixturesInput = []) |
@@ -84,12 +84,8 @@ class MessageController extends Controller | |||
/** | |||
* @var array list of patterns that specify which files/directories should NOT be processed. | |||
* If empty or not set, all files/directories will be processed. | |||
* A path matches a pattern if it contains the pattern string at its end. For example, | |||
* '/a/b' will match all files and directories ending with '/a/b'; | |||
* the '*.svn' will match all files and directories whose name ends with '.svn'. | |||
* and the '.svn' will match all files and directories named exactly '.svn'. | |||
* Note, the '/' characters in a pattern matches both '/' and '\'. | |||
* See helpers/FileHelper::findFiles() description for more details on pattern matching rules. | |||
* See helpers/FileHelper::findFiles() description for pattern matching rules. | |||
* If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. | |||
*/ | |||
public $except = [ | |||
'.svn', | |||
@@ -104,7 +100,7 @@ class MessageController extends Controller | |||
/** | |||
* @var array list of patterns that specify which files (not directories) should be processed. | |||
* If empty or not set, all files will be processed. | |||
* Please refer to "except" for details about the patterns. | |||
* See helpers/FileHelper::findFiles() description for pattern matching rules. | |||
* If a file/directory matches both a pattern in "only" and "except", it will NOT be processed. | |||
*/ | |||
public $only = ['*.php']; | |||
@@ -215,7 +211,7 @@ class MessageController extends Controller | |||
* You may modify this file to suit your needs. | |||
* | |||
* You can use 'yii {$this->id}/{$this->action->id}-template' command to create | |||
* template configuration file with detaild description for each parameter. | |||
* template configuration file with detailed description for each parameter. | |||
*/ | |||
return $array; | |||
@@ -453,10 +449,10 @@ EOD; | |||
$subject = file_get_contents($fileName); | |||
$messages = []; | |||
$tokens = token_get_all($subject); | |||
foreach ((array) $translator as $currentTranslator) { | |||
$translatorTokens = token_get_all('<?php ' . $currentTranslator); | |||
array_shift($translatorTokens); | |||
$tokens = token_get_all($subject); | |||
$messages = array_merge_recursive($messages, $this->extractMessagesFromTokens($tokens, $translatorTokens, $ignoreCategories)); | |||
} | |||
@@ -644,6 +640,7 @@ EOD; | |||
* @param boolean $sort if translations should be sorted | |||
* @param string $category message category | |||
* @param boolean $markUnused if obsolete translations should be marked | |||
* @return int exit code | |||
*/ | |||
protected function saveMessagesCategoryToPHP($messages, $fileName, $overwrite, $removeUnused, $sort, $category, $markUnused) | |||
{ | |||
@@ -654,7 +651,7 @@ EOD; | |||
ksort($existingMessages); | |||
if (array_keys($existingMessages) === $messages && (!$sort || array_keys($rawExistingMessages) === $messages)) { | |||
$this->stdout("Nothing new in \"$category\" category... Nothing to save.\n\n", Console::FG_GREEN); | |||
return; | |||
return self::EXIT_CODE_NORMAL; | |||
} | |||
unset($rawExistingMessages); | |||
$merged = []; | |||
@@ -855,11 +852,11 @@ EOD; | |||
foreach ($msgs as $message) { | |||
$merged[$category . chr(4) . $message] = ''; | |||
} | |||
ksort($merged); | |||
$this->stdout("Category \"$category\" merged.\n"); | |||
$hasSomethingToWrite = true; | |||
} | |||
if ($hasSomethingToWrite) { | |||
ksort($merged); | |||
$poFile->save($file, $merged); | |||
$this->stdout("Translation saved.\n", Console::FG_GREEN); | |||
} else { |
@@ -50,6 +50,24 @@ use yii\helpers\Console; | |||
* yii migrate/down | |||
* ``` | |||
* | |||
* Since 2.0.10 you can use namespaced migrations. In order to enable this feature you should configure [[migrationNamespaces]] | |||
* property for the controller at application configuration: | |||
* | |||
* ```php | |||
* return [ | |||
* 'controllerMap' => [ | |||
* 'migrate' => [ | |||
* 'class' => 'yii\console\controllers\MigrateController', | |||
* 'migrationNamespaces' => [ | |||
* 'app\migrations', | |||
* 'some\extension\migrations', | |||
* ], | |||
* //'migrationPath' => null, // allows to disable not namespaced migration completely | |||
* ], | |||
* ], | |||
* ]; | |||
* ``` | |||
* | |||
* @author Qiang Xue <qiang.xue@gmail.com> | |||
* @since 2.0 | |||
*/ | |||
@@ -164,8 +182,11 @@ class MigrateController extends BaseMigrateController | |||
*/ | |||
protected function createMigration($class) | |||
{ | |||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php'; | |||
require_once($file); | |||
$class = trim($class, '\\'); | |||
if (strpos($class, '\\') === false) { | |||
$file = $this->migrationPath . DIRECTORY_SEPARATOR . $class . '.php'; | |||
require_once($file); | |||
} | |||
return new $class(['db' => $this->db]); | |||
} | |||
@@ -351,7 +372,7 @@ class MigrateController extends BaseMigrateController | |||
continue; | |||
} | |||
if (!preg_match('/^(.+?)\(([^)]+)\)$/', $chunk)) { | |||
if (!preg_match('/^(.+?)\(([^(]+)\)$/', $chunk)) { | |||
$chunk .= '()'; | |||
} | |||
} | |||
@@ -375,7 +396,7 @@ class MigrateController extends BaseMigrateController | |||
protected function addDefaultPrimaryKey(&$fields) | |||
{ | |||
foreach ($fields as $field) { | |||
if ($field['decorators'] === 'primaryKey()') { | |||
if ($field['decorators'] === 'primaryKey()' || $field['decorators'] === 'bigPrimaryKey()') { | |||
return; | |||
} | |||
} |
@@ -0,0 +1 @@ | |||
* |
@@ -18,8 +18,8 @@ use yii\base\InvalidParamException; | |||
* @property array $keys The list of key values corresponding to [[models]]. Each data model in [[models]] is | |||
* uniquely identified by the corresponding key value in this array. | |||
* @property array $models The list of data models in the current page. | |||
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination | |||
* is disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and | |||
* @property Pagination|false $pagination The pagination object. If this is false, it means the pagination is | |||
* disabled. Note that the type of this property differs in getter and setter. See [[getPagination()]] and | |||
* [[setPagination()]] for details. | |||
* @property Sort|boolean $sort The sorting object. If this is false, it means the sorting is disabled. Note | |||
* that the type of this property differs in getter and setter. See [[getSort()]] and [[setSort()]] for details. | |||
@@ -163,7 +163,7 @@ abstract class BaseDataProvider extends Component implements DataProviderInterfa | |||
* Returns the pagination object used by this data provider. | |||
* Note that you should call [[prepare()]] or [[getModels()]] first to get correct values | |||
* of [[Pagination::totalCount]] and [[Pagination::pageCount]]. | |||
* @return Pagination|boolean the pagination object. If this is false, it means the pagination is disabled. | |||
* @return Pagination|false the pagination object. If this is false, it means the pagination is disabled. | |||
*/ | |||
public function getPagination() | |||
{ |
@@ -27,7 +27,7 @@ use yii\web\Request; | |||
* Controller action: | |||
* | |||
* ```php | |||
* function actionIndex() | |||
* public function actionIndex() | |||
* { | |||
* $query = Article::find()->where(['status' => 1]); | |||
* $countQuery = clone $query; |
@@ -24,7 +24,7 @@ use yii\web\Request; | |||
* A typical usage example is as follows, | |||
* | |||
* ```php | |||
* function actionIndex() | |||
* public function actionIndex() | |||
* { | |||
* $sort = new Sort([ | |||
* 'attributes' => [ | |||
@@ -61,13 +61,14 @@ use yii\web\Request; | |||
* } | |||
* ``` | |||
* | |||
* In the above, we declare two [[attributes]] that support sorting: name and age. | |||
* In the above, we declare two [[attributes]] that support sorting: `name` and `age`. | |||
* We pass the sort information to the Article query so that the query results are | |||
* sorted by the orders specified by the Sort object. In the view, we show two hyperlinks | |||
* that can lead to pages with the data sorted by the corresponding attributes. | |||
* | |||
* @property array $attributeOrders Sort directions indexed by attribute names. Sort direction can be either | |||
* `SORT_ASC` for ascending order or `SORT_DESC` for descending order. This property is read-only. | |||
* `SORT_ASC` for ascending order or `SORT_DESC` for descending order. Note that the type of this property | |||
* differs in getter and setter. See [[getAttributeOrders()]] and [[setAttributeOrders()]] for details. | |||
* @property array $orders The columns (keys) and their corresponding sort directions (values). This can be | |||
* passed to [[\yii\db\Query::orderBy()]] to construct a DB query. This property is read-only. | |||
* | |||
@@ -78,7 +79,7 @@ class Sort extends Object | |||
{ | |||
/** | |||
* @var boolean whether the sorting can be applied to multiple attributes simultaneously. | |||
* Defaults to false, which means each time the data can only be sorted by one attribute. | |||
* Defaults to `false`, which means each time the data can only be sorted by one attribute. | |||
*/ | |||
public $enableMultiSort = false; | |||
/** | |||
@@ -97,7 +98,7 @@ class Sort extends Object | |||
* ] | |||
* ``` | |||
* | |||
* In the above, two attributes are declared: "age" and "name". The "age" attribute is | |||
* In the above, two attributes are declared: `age` and `name`. The `age` attribute is | |||
* a simple attribute which is equivalent to the following: | |||
* | |||
* ```php | |||
@@ -109,16 +110,16 @@ class Sort extends Object | |||
* ] | |||
* ``` | |||
* | |||
* The "name" attribute is a composite attribute: | |||
* The `name` attribute is a composite attribute: | |||
* | |||
* - The "name" key represents the attribute name which will appear in the URLs leading | |||
* - The `name` key represents the attribute name which will appear in the URLs leading | |||
* to sort actions. | |||
* - The "asc" and "desc" elements specify how to sort by the attribute in ascending | |||
* - The `asc` and `desc` elements specify how to sort by the attribute in ascending | |||
* and descending orders, respectively. Their values represent the actual columns and | |||
* the directions by which the data should be sorted by. | |||
* - The "default" element specifies by which direction the attribute should be sorted | |||
* - The `default` element specifies by which direction the attribute should be sorted | |||
* if it is not currently sorted (the default value is ascending order). | |||
* - The "label" element specifies what label should be used when calling [[link()]] to create | |||
* - The `label` element specifies what label should be used when calling [[link()]] to create | |||
* a sort link. If not set, [[Inflector::camel2words()]] will be called to get a label. | |||
* Note that it will not be HTML-encoded. | |||
* | |||
@@ -128,7 +129,7 @@ class Sort extends Object | |||
public $attributes = []; | |||
/** | |||
* @var string the name of the parameter that specifies which attributes to be sorted | |||
* in which direction. Defaults to 'sort'. | |||
* in which direction. Defaults to `sort`. | |||
* @see params | |||
*/ | |||
public $sortParam = 'sort'; | |||
@@ -157,7 +158,7 @@ class Sort extends Object | |||
public $separator = ','; | |||
/** | |||
* @var array parameters (name => value) that should be used to obtain the current sort directions | |||
* and to create new sort URLs. If not set, $_GET will be used instead. | |||
* and to create new sort URLs. If not set, `$_GET` will be used instead. | |||
* | |||
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`. | |||
* | |||
@@ -170,7 +171,7 @@ class Sort extends Object | |||
public $params; | |||
/** | |||
* @var \yii\web\UrlManager the URL manager used for creating sort URLs. If not set, | |||
* the "urlManager" application component will be used. | |||
* the `urlManager` application component will be used. | |||
*/ | |||
public $urlManager; | |||
@@ -265,6 +266,32 @@ class Sort extends Object | |||
return $this->_attributeOrders; | |||
} | |||
/** | |||
* Sets up the currently sort information. | |||
* @param array|null $attributeOrders sort directions indexed by attribute names. | |||
* Sort direction can be either `SORT_ASC` for ascending order or | |||
* `SORT_DESC` for descending order. | |||
* @param boolean $validate whether to validate given attribute orders against [[attributes]] and [[enableMultiSort]]. | |||
* If validation is enabled incorrect entries will be removed. | |||
* @since 2.0.10 | |||
*/ | |||
public function setAttributeOrders($attributeOrders, $validate = true) | |||
{ | |||
if ($attributeOrders === null || !$validate) { | |||
$this->_attributeOrders = $attributeOrders; | |||
} else { | |||
$this->_attributeOrders = []; | |||
foreach ($attributeOrders as $attribute => $order) { | |||
if (isset($this->attributes[$attribute])) { | |||
$this->_attributeOrders[$attribute] = $order; | |||
if (!$this->enableMultiSort) { | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Returns the sort direction of the specified attribute in the current request. | |||
* @param string $attribute the attribute name |
@@ -28,6 +28,16 @@ interface ActiveQueryInterface extends QueryInterface | |||
* @return $this the query object itself | |||
*/ | |||
public function asArray($value = true); | |||
/** | |||
* Executes query and returns a single row of result. | |||
* @param Connection $db the DB connection used to create the DB command. | |||
* If `null`, the DB connection returned by [[modelClass]] will be used. | |||
* @return ActiveRecordInterface|array|null a single row of query result. Depending on the setting of [[asArray]], | |||
* the query result may be either an array or an ActiveRecord object. `null` will be returned | |||
* if the query results in nothing. | |||
*/ | |||
public function one($db = null); | |||
/** | |||
* Sets the [[indexBy]] property. |
@@ -274,8 +274,8 @@ class ActiveRecord extends BaseActiveRecord | |||
/** | |||
* Declares the name of the database table associated with this AR class. | |||
* By default this method returns the class name as the table name by calling [[Inflector::camel2id()]] | |||
* with prefix [[Connection::tablePrefix]]. For example if [[Connection::tablePrefix]] is 'tbl_', | |||
* 'Customer' becomes 'tbl_customer', and 'OrderItem' becomes 'tbl_order_item'. You may override this method | |||
* with prefix [[Connection::tablePrefix]]. For example if [[Connection::tablePrefix]] is `tbl_`, | |||
* `Customer` becomes `tbl_customer`, and `OrderItem` becomes `tbl_order_item`. You may override this method | |||
* if the table is not named after this convention. | |||
* @return string the table name | |||
*/ | |||
@@ -381,9 +381,9 @@ class ActiveRecord extends BaseActiveRecord | |||
* | |||
* This method performs the following steps in order: | |||
* | |||
* 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]] | |||
* 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] | |||
* returns `false`, the rest of the steps will be skipped; | |||
* 2. call [[afterValidate()]] when `$runValidation` is true. If validation | |||
* 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation | |||
* failed, the rest of the steps will be skipped; | |||
* 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, | |||
* the rest of the steps will be skipped; | |||
@@ -396,7 +396,7 @@ class ActiveRecord extends BaseActiveRecord | |||
* | |||
* Only the [[dirtyAttributes|changed attribute values]] will be inserted into database. | |||
* | |||
* If the table's primary key is auto-incremental and is null during insertion, | |||
* If the table's primary key is auto-incremental and is `null` during insertion, | |||
* it will be populated with the actual value after insertion. | |||
* | |||
* For example, to insert a customer record: | |||
@@ -411,7 +411,7 @@ class ActiveRecord extends BaseActiveRecord | |||
* @param boolean $runValidation whether to perform validation (calling [[validate()]]) | |||
* before saving the record. Defaults to `true`. If the validation fails, the record | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributes list of attributes that need to be saved. Defaults to null, | |||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return boolean whether the attributes are valid and the record is inserted successfully. | |||
* @throws \Exception in case insert failed. | |||
@@ -444,7 +444,7 @@ class ActiveRecord extends BaseActiveRecord | |||
/** | |||
* Inserts an ActiveRecord into DB without considering transaction. | |||
* @param array $attributes list of attributes that need to be saved. Defaults to null, | |||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return boolean whether the record is inserted successfully. | |||
*/ | |||
@@ -475,9 +475,9 @@ class ActiveRecord extends BaseActiveRecord | |||
* | |||
* This method performs the following steps in order: | |||
* | |||
* 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]] | |||
* 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] | |||
* returns `false`, the rest of the steps will be skipped; | |||
* 2. call [[afterValidate()]] when `$runValidation` is true. If validation | |||
* 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation | |||
* failed, the rest of the steps will be skipped; | |||
* 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, | |||
* the rest of the steps will be skipped; | |||
@@ -514,9 +514,9 @@ class ActiveRecord extends BaseActiveRecord | |||
* @param boolean $runValidation whether to perform validation (calling [[validate()]]) | |||
* before saving the record. Defaults to `true`. If the validation fails, the record | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributeNames list of attributes that need to be saved. Defaults to null, | |||
* @param array $attributeNames list of attributes that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return integer|boolean the number of rows affected, or false if validation fails | |||
* @return integer|false the number of rows affected, or false if validation fails | |||
* or [[beforeSave()]] stops the updating process. | |||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data | |||
* being updated is outdated. | |||
@@ -553,7 +553,7 @@ class ActiveRecord extends BaseActiveRecord | |||
* | |||
* This method performs the following steps in order: | |||
* | |||
* 1. call [[beforeDelete()]]. If the method returns false, it will skip the | |||
* 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the | |||
* rest of the steps; | |||
* 2. delete the record from the database; | |||
* 3. call [[afterDelete()]]. | |||
@@ -561,7 +561,7 @@ class ActiveRecord extends BaseActiveRecord | |||
* In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]] | |||
* will be raised by the corresponding methods. | |||
* | |||
* @return integer|false the number of rows deleted, or false if the deletion is unsuccessful for some reason. | |||
* @return integer|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. | |||
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. | |||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data | |||
* being deleted is outdated. | |||
@@ -590,7 +590,7 @@ class ActiveRecord extends BaseActiveRecord | |||
/** | |||
* Deletes an ActiveRecord without considering transaction. | |||
* @return integer|false the number of rows deleted, or false if the deletion is unsuccessful for some reason. | |||
* @return integer|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. | |||
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. | |||
* @throws StaleObjectException | |||
*/ |
@@ -36,9 +36,9 @@ interface ActiveRecordInterface | |||
/** | |||
* Returns the named attribute value. | |||
* If this record is the result of a query and the attribute is not loaded, | |||
* null will be returned. | |||
* `null` will be returned. | |||
* @param string $name the attribute name | |||
* @return mixed the attribute value. Null if the attribute is not set or does not exist. | |||
* @return mixed the attribute value. `null` if the attribute is not set or does not exist. | |||
* @see hasAttribute() | |||
*/ | |||
public function getAttribute($name); | |||
@@ -64,8 +64,8 @@ interface ActiveRecordInterface | |||
* the return value will be an array with attribute names as keys and attribute values as values. | |||
* Note that for composite primary keys, an array will always be returned regardless of this parameter value. | |||
* @return mixed the primary key value. An array (attribute name => attribute value) is returned if the primary key | |||
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if | |||
* the key value is null). | |||
* is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if | |||
* the key value is `null`). | |||
*/ | |||
public function getPrimaryKey($asArray = false); | |||
@@ -76,13 +76,13 @@ interface ActiveRecordInterface | |||
* The value remains unchanged even if the primary key attribute is manually assigned with a different value. | |||
* @param boolean $asArray whether to return the primary key value as an array. If true, | |||
* the return value will be an array with column name as key and column value as value. | |||
* If this is false (default), a scalar value will be returned for non-composite primary key. | |||
* If this is `false` (default), a scalar value will be returned for non-composite primary key. | |||
* @property mixed The old primary key value. An array (column name => column value) is | |||
* returned if the primary key is composite. A string is returned otherwise (null will be | |||
* returned if the key value is null). | |||
* returned if the primary key is composite. A string is returned otherwise (`null` will be | |||
* returned if the key value is `null`). | |||
* @return mixed the old primary key value. An array (column name => column value) is returned if the primary key | |||
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if | |||
* the key value is null). | |||
* is composite or `$asArray` is true. A string is returned otherwise (`null` will be returned if | |||
* the key value is `null`). | |||
*/ | |||
public function getOldPrimaryKey($asArray = false); | |||
@@ -156,11 +156,11 @@ interface ActiveRecordInterface | |||
* The method accepts: | |||
* | |||
* - a scalar value (integer or string): query by a single primary key value and return the | |||
* corresponding record (or null if not found). | |||
* corresponding record (or `null` if not found). | |||
* - a non-associative array: query by a list of primary key values and return the | |||
* first record (or null if not found). | |||
* first record (or `null` if not found). | |||
* - an associative array of name-value pairs: query by a set of attribute values and return a single record | |||
* matching all of them (or null if not found). Note that `['id' => 1, 2]` is treated as a non-associative array. | |||
* matching all of them (or `null` if not found). Note that `['id' => 1, 2]` is treated as a non-associative array. | |||
* | |||
* That this method will automatically call the `one()` method and return an [[ActiveRecordInterface|ActiveRecord]] | |||
* instance. For example, | |||
@@ -180,7 +180,7 @@ interface ActiveRecordInterface | |||
* ``` | |||
* | |||
* @param mixed $condition primary key value or a set of column values | |||
* @return static|null ActiveRecord instance matching the condition, or null if nothing matches. | |||
* @return static ActiveRecord instance matching the condition, or `null` if nothing matches. | |||
*/ | |||
public static function findOne($condition); | |||
@@ -276,10 +276,10 @@ interface ActiveRecordInterface | |||
* $customer->save(); | |||
* ``` | |||
* | |||
* @param boolean $runValidation whether to perform validation (calling [[validate()]]) | |||
* @param boolean $runValidation whether to perform validation (calling [[Model::validate()|validate()]]) | |||
* before saving the record. Defaults to `true`. If the validation fails, the record | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributeNames list of attribute names that need to be saved. Defaults to null, | |||
* @param array $attributeNames list of attribute names that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return boolean whether the saving succeeded (i.e. no validation errors occurred). | |||
*/ | |||
@@ -297,10 +297,10 @@ interface ActiveRecordInterface | |||
* $customer->insert(); | |||
* ``` | |||
* | |||
* @param boolean $runValidation whether to perform validation (calling [[validate()]]) | |||
* @param boolean $runValidation whether to perform validation (calling [[Model::validate()|validate()]]) | |||
* before saving the record. Defaults to `true`. If the validation fails, the record | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributes list of attributes that need to be saved. Defaults to null, | |||
* @param array $attributes list of attributes that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return boolean whether the attributes are valid and the record is inserted successfully. | |||
*/ | |||
@@ -318,12 +318,12 @@ interface ActiveRecordInterface | |||
* $customer->update(); | |||
* ``` | |||
* | |||
* @param boolean $runValidation whether to perform validation (calling [[validate()]]) | |||
* @param boolean $runValidation whether to perform validation (calling [[Model::validate()|validate()]]) | |||
* before saving the record. Defaults to `true`. If the validation fails, the record | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributeNames list of attributes that need to be saved. Defaults to null, | |||
* @param array $attributeNames list of attributes that need to be saved. Defaults to `null`, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return integer|boolean the number of rows affected, or false if validation fails | |||
* @return integer|boolean the number of rows affected, or `false` if validation fails | |||
* or updating process is stopped for other reasons. | |||
* Note that it is possible that the number of rows affected is 0, even though the | |||
* update execution is successful. | |||
@@ -333,7 +333,7 @@ interface ActiveRecordInterface | |||
/** | |||
* Deletes the record from the database. | |||
* | |||
* @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason. | |||
* @return integer|boolean the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. | |||
* Note that it is possible that the number of rows deleted is 0, even though the deletion execution is successful. | |||
*/ | |||
public function delete(); | |||
@@ -382,13 +382,13 @@ interface ActiveRecordInterface | |||
* If the relationship involves a junction table, a new row will be inserted into the | |||
* junction table which contains the primary key values from both records. | |||
* | |||
* This method requires that the primary key value is not null. | |||
* This method requires that the primary key value is not `null`. | |||
* | |||
* @param string $name the case sensitive name of the relationship. | |||
* @param static $model the record to be linked with the current one. | |||
* @param array $extraColumns additional column values to be saved into the junction table. | |||
* This parameter is only meaningful for a relationship involving a junction table | |||
* (i.e., a relation set with `[[ActiveQueryInterface::via()]]`.) | |||
* (i.e., a relation set with [[ActiveQueryInterface::via()]]). | |||
*/ | |||
public function link($name, $model, $extraColumns = []); | |||
@@ -396,12 +396,12 @@ interface ActiveRecordInterface | |||
* Destroys the relationship between two records. | |||
* | |||
* The record with the foreign key of the relationship will be deleted if `$delete` is true. | |||
* Otherwise, the foreign key will be set null and the record will be saved without validation. | |||
* Otherwise, the foreign key will be set `null` and the record will be saved without validation. | |||
* | |||
* @param string $name the case sensitive name of the relationship. | |||
* @param static $model the model to be unlinked from the current one. | |||
* @param boolean $delete whether to delete the model that contains the foreign key. | |||
* If false, the model's foreign key will be set null and saved. | |||
* If false, the model's foreign key will be set `null` and saved. | |||
* If true, the model containing the foreign key will be deleted. | |||
*/ | |||
public function unlink($name, $model, $delete = false); |
@@ -52,7 +52,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
const EVENT_AFTER_FIND = 'afterFind'; | |||
/** | |||
* @event ModelEvent an event that is triggered before inserting a record. | |||
* You may set [[ModelEvent::isValid]] to be false to stop the insertion. | |||
* You may set [[ModelEvent::isValid]] to be `false` to stop the insertion. | |||
*/ | |||
const EVENT_BEFORE_INSERT = 'beforeInsert'; | |||
/** | |||
@@ -61,7 +61,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
const EVENT_AFTER_INSERT = 'afterInsert'; | |||
/** | |||
* @event ModelEvent an event that is triggered before updating a record. | |||
* You may set [[ModelEvent::isValid]] to be false to stop the update. | |||
* You may set [[ModelEvent::isValid]] to be `false` to stop the update. | |||
*/ | |||
const EVENT_BEFORE_UPDATE = 'beforeUpdate'; | |||
/** | |||
@@ -70,7 +70,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
const EVENT_AFTER_UPDATE = 'afterUpdate'; | |||
/** | |||
* @event ModelEvent an event that is triggered before deleting a record. | |||
* You may set [[ModelEvent::isValid]] to be false to stop the deletion. | |||
* You may set [[ModelEvent::isValid]] to be `false` to stop the deletion. | |||
*/ | |||
const EVENT_BEFORE_DELETE = 'beforeDelete'; | |||
/** | |||
@@ -100,7 +100,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* @inheritdoc | |||
* @return static|null ActiveRecord instance matching the condition, or `null` if nothing matches. | |||
* @return static ActiveRecord instance matching the condition, or `null` if nothing matches. | |||
*/ | |||
public static function findOne($condition) | |||
{ | |||
@@ -223,13 +223,47 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* to resolve the conflict. | |||
* | |||
* @return string the column name that stores the lock version of a table row. | |||
* If null is returned (default implemented), optimistic locking will not be supported. | |||
* If `null` is returned (default implemented), optimistic locking will not be supported. | |||
*/ | |||
public function optimisticLock() | |||
{ | |||
return null; | |||
} | |||
/** | |||
* @inheritdoc | |||
*/ | |||
public function canGetProperty($name, $checkVars = true, $checkBehaviors = true) | |||
{ | |||
if (parent::canGetProperty($name, $checkVars, $checkBehaviors)) { | |||
return true; | |||
} | |||
try { | |||
return $this->hasAttribute($name); | |||
} catch (\Exception $e) { | |||
// `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used | |||
return false; | |||
} | |||
} | |||
/** | |||
* @inheritdoc | |||
*/ | |||
public function canSetProperty($name, $checkVars = true, $checkBehaviors = true) | |||
{ | |||
if (parent::canSetProperty($name, $checkVars, $checkBehaviors)) { | |||
return true; | |||
} | |||
try { | |||
return $this->hasAttribute($name); | |||
} catch (\Exception $e) { | |||
// `hasAttribute()` may fail on base/abstract classes in case automatic attribute list fetching used | |||
return false; | |||
} | |||
} | |||
/** | |||
* PHP getter magic method. | |||
* This method is overridden so that attributes and related objects can be accessed like properties. | |||
@@ -275,7 +309,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Checks if a property value is null. | |||
* This method overrides the parent implementation by checking if the named attribute is null or not. | |||
* This method overrides the parent implementation by checking if the named attribute is `null` or not. | |||
* @param string $name the property name or the event name | |||
* @return boolean whether the property value is null | |||
*/ | |||
@@ -430,9 +464,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Returns the named attribute value. | |||
* If this record is the result of a query and the attribute is not loaded, | |||
* null will be returned. | |||
* `null` will be returned. | |||
* @param string $name the attribute name | |||
* @return mixed the attribute value. Null if the attribute is not set or does not exist. | |||
* @return mixed the attribute value. `null` if the attribute is not set or does not exist. | |||
* @see hasAttribute() | |||
*/ | |||
public function getAttribute($name) | |||
@@ -479,9 +513,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Returns the old value of the named attribute. | |||
* If this record is the result of a query and the attribute is not loaded, | |||
* null will be returned. | |||
* `null` will be returned. | |||
* @param string $name the attribute name | |||
* @return mixed the old attribute value. Null if the attribute is not loaded before | |||
* @return mixed the old attribute value. `null` if the attribute is not loaded before | |||
* or does not exist. | |||
* @see hasAttribute() | |||
*/ | |||
@@ -573,8 +607,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Saves the current record. | |||
* | |||
* This method will call [[insert()]] when [[isNewRecord]] is true, or [[update()]] | |||
* when [[isNewRecord]] is false. | |||
* This method will call [[insert()]] when [[isNewRecord]] is `true`, or [[update()]] | |||
* when [[isNewRecord]] is `false`. | |||
* | |||
* For example, to save a customer record: | |||
* | |||
@@ -606,9 +640,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* | |||
* This method performs the following steps in order: | |||
* | |||
* 1. call [[beforeValidate()]] when `$runValidation` is true. If [[beforeValidate()]] | |||
* 1. call [[beforeValidate()]] when `$runValidation` is `true`. If [[beforeValidate()]] | |||
* returns `false`, the rest of the steps will be skipped; | |||
* 2. call [[afterValidate()]] when `$runValidation` is true. If validation | |||
* 2. call [[afterValidate()]] when `$runValidation` is `true`. If validation | |||
* failed, the rest of the steps will be skipped; | |||
* 3. call [[beforeSave()]]. If [[beforeSave()]] returns `false`, | |||
* the rest of the steps will be skipped; | |||
@@ -647,7 +681,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* will not be saved to the database and this method will return `false`. | |||
* @param array $attributeNames list of attribute names that need to be saved. Defaults to null, | |||
* meaning all attributes that are loaded from DB will be saved. | |||
* @return integer|boolean the number of rows affected, or false if validation fails | |||
* @return integer|false the number of rows affected, or `false` if validation fails | |||
* or [[beforeSave()]] stops the updating process. | |||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data | |||
* being updated is outdated. | |||
@@ -689,7 +723,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
} | |||
$values = $this->getDirtyAttributes($attrs); | |||
if (empty($values)) { | |||
if (empty($values) || $this->getIsNewRecord()) { | |||
return 0; | |||
} | |||
@@ -705,7 +739,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* @see update() | |||
* @param array $attributes attributes to update | |||
* @return integer number of rows updated | |||
* @return integer|false the number of rows affected, or false if [[beforeSave()]] stops the updating process. | |||
* @throws StaleObjectException | |||
*/ | |||
protected function updateInternal($attributes = null) | |||
@@ -785,7 +819,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* | |||
* This method performs the following steps in order: | |||
* | |||
* 1. call [[beforeDelete()]]. If the method returns false, it will skip the | |||
* 1. call [[beforeDelete()]]. If the method returns `false`, it will skip the | |||
* rest of the steps; | |||
* 2. delete the record from the database; | |||
* 3. call [[afterDelete()]]. | |||
@@ -793,7 +827,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* In the above step 1 and 3, events named [[EVENT_BEFORE_DELETE]] and [[EVENT_AFTER_DELETE]] | |||
* will be raised by the corresponding methods. | |||
* | |||
* @return integer|false the number of rows deleted, or false if the deletion is unsuccessful for some reason. | |||
* @return integer|false the number of rows deleted, or `false` if the deletion is unsuccessful for some reason. | |||
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful. | |||
* @throws StaleObjectException if [[optimisticLock|optimistic locking]] is enabled and the data | |||
* being deleted is outdated. | |||
@@ -866,8 +900,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* This method is called at the beginning of inserting or updating a record. | |||
* The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is true, | |||
* or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is false. | |||
* The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is `true`, | |||
* or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is `false`. | |||
* When overriding this method, make sure you call the parent implementation like the following: | |||
* | |||
* ```php | |||
@@ -883,9 +917,9 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* ``` | |||
* | |||
* @param boolean $insert whether this method called while inserting a record. | |||
* If false, it means the method is called while updating a record. | |||
* If `false`, it means the method is called while updating a record. | |||
* @return boolean whether the insertion or updating should continue. | |||
* If false, the insertion or updating will be cancelled. | |||
* If `false`, the insertion or updating will be cancelled. | |||
*/ | |||
public function beforeSave($insert) | |||
{ | |||
@@ -897,12 +931,12 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* This method is called at the end of inserting or updating a record. | |||
* The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is true, | |||
* or an [[EVENT_AFTER_UPDATE]] event if `$insert` is false. The event class used is [[AfterSaveEvent]]. | |||
* The default implementation will trigger an [[EVENT_AFTER_INSERT]] event when `$insert` is `true`, | |||
* or an [[EVENT_AFTER_UPDATE]] event if `$insert` is `false`. The event class used is [[AfterSaveEvent]]. | |||
* When overriding this method, make sure you call the parent implementation so that | |||
* the event is triggered. | |||
* @param boolean $insert whether this method called while inserting a record. | |||
* If false, it means the method is called while updating a record. | |||
* If `false`, it means the method is called while updating a record. | |||
* @param array $changedAttributes The old values of attributes that had changed and were saved. | |||
* You can use this parameter to take action based on the changes made for example send an email | |||
* when the password had changed or implement audit trail that tracks all the changes. | |||
@@ -933,7 +967,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* } | |||
* ``` | |||
* | |||
* @return boolean whether the record should be deleted. Defaults to true. | |||
* @return boolean whether the record should be deleted. Defaults to `true`. | |||
*/ | |||
public function beforeDelete() | |||
{ | |||
@@ -960,7 +994,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* If the refresh is successful, an [[EVENT_AFTER_REFRESH]] event will be triggered. | |||
* This event is available since version 2.0.8. | |||
* | |||
* @return boolean whether the row still exists in the database. If true, the latest data | |||
* @return boolean whether the row still exists in the database. If `true`, the latest data | |||
* will be populated to this active record. Otherwise, this record will remain unchanged. | |||
*/ | |||
public function refresh() | |||
@@ -973,7 +1007,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
foreach ($this->attributes() as $name) { | |||
$this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null; | |||
} | |||
$this->_oldAttributes = $this->_attributes; | |||
$this->_oldAttributes = $record->_oldAttributes; | |||
$this->_related = []; | |||
$this->afterRefresh(); | |||
@@ -1010,14 +1044,14 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Returns the primary key value(s). | |||
* @param boolean $asArray whether to return the primary key value as an array. If true, | |||
* @param boolean $asArray whether to return the primary key value as an array. If `true`, | |||
* the return value will be an array with column names as keys and column values as values. | |||
* Note that for composite primary keys, an array will always be returned regardless of this parameter value. | |||
* @property mixed The primary key value. An array (column name => column value) is returned if | |||
* the primary key is composite. A string is returned otherwise (null will be returned if | |||
* the key value is null). | |||
* @return mixed the primary key value. An array (column name => column value) is returned if the primary key | |||
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if | |||
* is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if | |||
* the key value is null). | |||
*/ | |||
public function getPrimaryKey($asArray = false) | |||
@@ -1040,14 +1074,14 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* This refers to the primary key value that is populated into the record | |||
* after executing a find method (e.g. find(), findOne()). | |||
* The value remains unchanged even if the primary key attribute is manually assigned with a different value. | |||
* @param boolean $asArray whether to return the primary key value as an array. If true, | |||
* @param boolean $asArray whether to return the primary key value as an array. If `true`, | |||
* the return value will be an array with column name as key and column value as value. | |||
* If this is false (default), a scalar value will be returned for non-composite primary key. | |||
* If this is `false` (default), a scalar value will be returned for non-composite primary key. | |||
* @property mixed The old primary key value. An array (column name => column value) is | |||
* returned if the primary key is composite. A string is returned otherwise (null will be | |||
* returned if the key value is null). | |||
* @return mixed the old primary key value. An array (column name => column value) is returned if the primary key | |||
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if | |||
* is composite or `$asArray` is `true`. A string is returned otherwise (null will be returned if | |||
* the key value is null). | |||
* @throws Exception if the AR model does not have a primary key | |||
*/ | |||
@@ -1132,7 +1166,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
* @param string $name the relation name | |||
* @param boolean $throwException whether to throw exception if the relation does not exist. | |||
* @return ActiveQueryInterface|ActiveQuery the relational query object. If the relation does not exist | |||
* and `$throwException` is false, null will be returned. | |||
* and `$throwException` is `false`, `null` will be returned. | |||
* @throws InvalidParamException if the named relation does not exist. | |||
*/ | |||
public function getRelation($name, $throwException = true) | |||
@@ -1272,16 +1306,16 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Destroys the relationship between two models. | |||
* | |||
* The model with the foreign key of the relationship will be deleted if `$delete` is true. | |||
* Otherwise, the foreign key will be set null and the model will be saved without validation. | |||
* The model with the foreign key of the relationship will be deleted if `$delete` is `true`. | |||
* Otherwise, the foreign key will be set `null` and the model will be saved without validation. | |||
* | |||
* @param string $name the case sensitive name of the relationship. | |||
* @param ActiveRecordInterface $model the model to be unlinked from the current one. | |||
* You have to make sure that the model is really related with the current model as this method | |||
* does not check this. | |||
* @param boolean $delete whether to delete the model that contains the foreign key. | |||
* If false, the model's foreign key will be set null and saved. | |||
* If true, the model containing the foreign key will be deleted. | |||
* If `false`, the model's foreign key will be set `null` and saved. | |||
* If `true`, the model containing the foreign key will be deleted. | |||
* @throws InvalidCallException if the models cannot be unlinked | |||
*/ | |||
public function unlink($name, $model, $delete = false) | |||
@@ -1371,8 +1405,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface | |||
/** | |||
* Destroys the relationship in current model. | |||
* | |||
* The model with the foreign key of the relationship will be deleted if `$delete` is true. | |||
* Otherwise, the foreign key will be set null and the model will be saved without validation. | |||
* The model with the foreign key of the relationship will be deleted if `$delete` is `true`. | |||
* Otherwise, the foreign key will be set `null` and the model will be saved without validation. | |||
* | |||
* Note that to destroy the relationship without removing records make sure your keys can be set to null | |||
* |
@@ -43,7 +43,7 @@ class ColumnSchemaBuilder extends Object | |||
* @var boolean|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added. | |||
* If this is `false`, a `NULL` constraint will be added. | |||
*/ | |||
protected $isNotNull = null; | |||
protected $isNotNull; | |||
/** | |||
* @var boolean whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added. | |||
*/ |
@@ -15,11 +15,11 @@ use yii\base\NotSupportedException; | |||
use yii\caching\Cache; | |||
/** | |||
* Connection represents a connection to a database via [PDO](php.net/manual/en/book.pdo.php). | |||
* Connection represents a connection to a database via [PDO](http://php.net/manual/en/book.pdo.php). | |||
* | |||
* Connection works together with [[Command]], [[DataReader]] and [[Transaction]] | |||
* to provide data access to various DBMS in a common set of APIs. They are a thin wrapper | |||
* of the [[PDO PHP extension]](php.net/manual/en/book.pdo.php). | |||
* of the [PDO PHP extension](http://php.net/manual/en/book.pdo.php). | |||
* | |||
* Connection supports database replication and read-write splitting. In particular, a Connection component | |||
* can be configured with multiple [[masters]] and [[slaves]]. It will do load balancing and failover by choosing | |||
@@ -27,7 +27,7 @@ use yii\caching\Cache; | |||
* the masters. | |||
* | |||
* To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then | |||
* call [[open()]] to be true. | |||
* call [[open()]] to connect to the database server. The current state of the connection can be checked using [[$isActive]]. | |||
* | |||
* The following example shows how to create a Connection instance and establish | |||
* the DB connection: | |||
@@ -222,7 +222,6 @@ class Connection extends Component | |||
public $enableQueryCache = true; | |||
/** | |||
* @var integer the default number of seconds that query results can remain valid in cache. | |||
* Use 0 to indicate that the cached data will never expire. | |||
* Defaults to 3600, meaning 3600 seconds, or one hour. Use 0 to indicate that the cached data will never expire. | |||
* The value of this property will be used when [[cache()]] is called without a cache duration. | |||
* @see enableQueryCache |
@@ -69,6 +69,7 @@ class Migration extends Component implements MigrationInterface | |||
parent::init(); | |||
$this->db = Instance::ensure($this->db, Connection::className()); | |||
$this->db->getSchema()->refresh(); | |||
$this->db->enableSlaves = false; | |||
} | |||
/** | |||
@@ -263,7 +264,7 @@ class Migration extends Component implements MigrationInterface | |||
$this->db->createCommand()->addCommentOnColumn($table, $column, $type->comment)->execute(); | |||
} | |||
} | |||
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n"; | |||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n"; | |||
} | |||
/** | |||
@@ -452,7 +453,7 @@ class Migration extends Component implements MigrationInterface | |||
*/ | |||
public function dropIndex($name, $table) | |||
{ | |||
echo " > drop index $name ..."; | |||
echo " > drop index $name on $table ..."; | |||
$time = microtime(true); | |||
$this->db->createCommand()->dropIndex($name, $table)->execute(); | |||
echo ' done (time: ' . sprintf('%.3f', microtime(true) - $time) . "s)\n"; | |||
@@ -464,7 +465,6 @@ class Migration extends Component implements MigrationInterface | |||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method. | |||
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method. | |||
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method. | |||
* @return $this the command object itself | |||
* @since 2.0.8 | |||
*/ | |||
public function addCommentOnColumn($table, $column, $comment) | |||
@@ -480,7 +480,6 @@ class Migration extends Component implements MigrationInterface | |||
* | |||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method. | |||
* @param string $comment the text of the comment to be added. The comment will be properly quoted by the method. | |||
* @return $this the command object itself | |||
* @since 2.0.8 | |||
*/ | |||
public function addCommentOnTable($table, $comment) | |||
@@ -496,7 +495,6 @@ class Migration extends Component implements MigrationInterface | |||
* | |||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method. | |||
* @param string $column the name of the column to be commented. The column name will be properly quoted by the method. | |||
* @return $this the command object itself | |||
* @since 2.0.8 | |||
*/ | |||
public function dropCommentFromColumn($table, $column) | |||
@@ -511,7 +509,6 @@ class Migration extends Component implements MigrationInterface | |||
* Builds a SQL statement for dropping comment from table | |||
* | |||
* @param string $table the table whose column is to be commented. The table name will be properly quoted by the method. | |||
* @return $this the command object itself | |||
* @since 2.0.8 | |||
*/ | |||
public function dropCommentFromTable($table) |
@@ -93,7 +93,7 @@ class Query extends Component implements QueryInterface | |||
*/ | |||
public $join; | |||
/** | |||
* @var string|array the condition to be applied in the GROUP BY clause. | |||
* @var string|array|Expression the condition to be applied in the GROUP BY clause. | |||
* It can be either a string or an array. Please refer to [[where()]] on how to specify the condition. | |||
*/ | |||
public $having; | |||
@@ -152,7 +152,7 @@ class Query extends Component implements QueryInterface | |||
* ```php | |||
* $query = (new Query)->from('user'); | |||
* foreach ($query->batch() as $rows) { | |||
* // $rows is an array of 10 or fewer rows from user table | |||
* // $rows is an array of 100 or fewer rows from user table | |||
* } | |||
* ``` | |||
* | |||
@@ -268,19 +268,22 @@ class Query extends Component implements QueryInterface | |||
*/ | |||
public function column($db = null) | |||
{ | |||
if (!is_string($this->indexBy)) { | |||
if ($this->indexBy === null) { | |||
return $this->createCommand($db)->queryColumn(); | |||
} | |||
if (is_array($this->select) && count($this->select) === 1) { | |||
if (is_string($this->indexBy) && is_array($this->select) && count($this->select) === 1) { | |||
$this->select[] = $this->indexBy; | |||
} | |||
$rows = $this->createCommand($db)->queryAll(); | |||
$results = []; | |||
foreach ($rows as $row) { | |||
if (array_key_exists($this->indexBy, $row)) { | |||
$results[$row[$this->indexBy]] = reset($row); | |||
$value = reset($row); | |||
if ($this->indexBy instanceof \Closure) { | |||
$results[call_user_func($this->indexBy, $row)] = $value; | |||
} else { | |||
$results[] = reset($row); | |||
$results[$row[$this->indexBy]] = $value; | |||
} | |||
} | |||
return $results; | |||
@@ -609,7 +612,7 @@ class Query extends Component implements QueryInterface | |||
*/ | |||
public function andFilterCompare($name, $value, $defaultOperator = '=') | |||
{ | |||
if (preg_match("/^(<>|>=|>|<=|<|=)/", $value, $matches)) { | |||
if (preg_match('/^(<>|>=|>|<=|<|=)/', $value, $matches)) { | |||
$operator = $matches[1]; | |||
$value = substr($value, strlen($operator)); | |||
} else { |
@@ -1279,7 +1279,7 @@ class QueryBuilder extends \yii\base\Object | |||
if (empty($vss)) { | |||
return $operator === 'IN' ? '0=1' : ''; | |||
}; | |||
} | |||
$sqlColumns = []; | |||
foreach ($columns as $i => $column) { |
@@ -37,9 +37,7 @@ use yii\caching\TagDependency; | |||
*/ | |||
abstract class Schema extends Object | |||
{ | |||
/** | |||
* The following are the supported abstract column data types. | |||
*/ | |||
// The following are the supported abstract column data types. | |||
const TYPE_PK = 'pk'; | |||
const TYPE_UPK = 'upk'; | |||
const TYPE_BIGPK = 'bigpk'; | |||
@@ -442,7 +440,7 @@ abstract class Schema extends Object | |||
* Executes the INSERT command, returning primary key values. | |||
* @param string $table the table that new rows will be inserted into. | |||
* @param array $columns the column data (name => value) to be inserted into the table. | |||
* @return array primary key values or false if the command fails | |||
* @return array|false primary key values or false if the command fails | |||
* @since 2.0.4 | |||
*/ | |||
public function insert($table, $columns) |
@@ -48,7 +48,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder | |||
*/ | |||
protected function buildCommentString() | |||
{ | |||
return $this->comment !== null ? " COMMENT " . $this->db->quoteValue($this->comment) : ''; | |||
return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : ''; | |||
} | |||
/** |
@@ -28,7 +28,7 @@ class QueryBuilder extends \yii\db\QueryBuilder | |||
Schema::TYPE_UBIGPK => 'bigint IDENTITY PRIMARY KEY', | |||
Schema::TYPE_CHAR => 'nchar(1)', | |||
Schema::TYPE_STRING => 'nvarchar(255)', | |||
Schema::TYPE_TEXT => 'ntext', | |||
Schema::TYPE_TEXT => 'nvarchar(max)', | |||
Schema::TYPE_SMALLINT => 'smallint', | |||
Schema::TYPE_INTEGER => 'int', | |||
Schema::TYPE_BIGINT => 'bigint', | |||
@@ -39,7 +39,7 @@ class QueryBuilder extends \yii\db\QueryBuilder | |||
Schema::TYPE_TIMESTAMP => 'timestamp', | |||
Schema::TYPE_TIME => 'time', | |||
Schema::TYPE_DATE => 'date', | |||
Schema::TYPE_BINARY => 'binary(1)', | |||
Schema::TYPE_BINARY => 'varbinary(max)', | |||
Schema::TYPE_BOOLEAN => 'bit', | |||
Schema::TYPE_MONEY => 'decimal(19,4)', | |||
]; | |||
@@ -236,8 +236,7 @@ class QueryBuilder extends \yii\db\QueryBuilder | |||
/* @var $model \yii\db\ActiveRecord */ | |||
$model = new $modelClass; | |||
$schema = $model->getTableSchema(); | |||
$columns = array_keys($schema->columns); | |||
return $columns; | |||
return array_keys($schema->columns); | |||
} | |||
/** |
@@ -152,7 +152,13 @@ class Schema extends \yii\db\Schema | |||
{ | |||
$parts = explode('.', str_replace(['[', ']'], '', $name)); | |||
$partCount = count($parts); | |||
if ($partCount === 3) { | |||
if ($partCount === 4) { | |||
// server name, catalog name, schema name and table name passed | |||
$table->catalogName = $parts[1]; | |||
$table->schemaName = $parts[2]; | |||
$table->name = $parts[3]; | |||
$table->fullName = $table->catalogName . '.' . $table->schemaName . '.' . $table->name; | |||
} elseif ($partCount === 3) { | |||
// catalog name, schema name and table name passed | |||
$table->catalogName = $parts[0]; | |||
$table->schemaName = $parts[1]; |
@@ -48,7 +48,7 @@ class ColumnSchemaBuilder extends AbstractColumnSchemaBuilder | |||
*/ | |||
protected function buildCommentString() | |||
{ | |||
return $this->comment !== null ? " COMMENT " . $this->db->quoteValue($this->comment) : ''; | |||
return $this->comment !== null ? ' COMMENT ' . $this->db->quoteValue($this->comment) : ''; | |||
} | |||
/** |
@@ -24,7 +24,7 @@ class QueryBuilder extends \yii\db\QueryBuilder | |||
*/ | |||
public $typeMap = [ | |||
Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY', | |||
Schema::TYPE_UPK => 'int(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', | |||
Schema::TYPE_UPK => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', | |||
Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY', | |||
Schema::TYPE_UBIGPK => 'bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', | |||
Schema::TYPE_CHAR => 'char(1)', | |||
@@ -209,7 +209,7 @@ class QueryBuilder extends \yii\db\QueryBuilder | |||
} | |||
} | |||
if (empty($names) && $tableSchema !== null) { | |||
$columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : reset($tableSchema->columns)->name; | |||
$columns = !empty($tableSchema->primaryKey) ? $tableSchema->primaryKey : [reset($tableSchema->columns)->name]; | |||
foreach ($columns as $name) { | |||
$names[] = $schema->quoteColumnName($name); | |||
$placeholders[] = 'DEFAULT'; |
@@ -243,6 +243,7 @@ class Schema extends \yii\db\Schema | |||
/** | |||
* Collects the foreign key column details for the given table. | |||
* @param TableSchema $table the table metadata | |||
* @throws \Exception | |||
*/ | |||
protected function findConstraints($table) | |||
{ |
@@ -471,7 +471,7 @@ SQL; | |||
$column->defaultValue = bindec(trim($column->defaultValue, 'B\'')); | |||
} elseif (preg_match("/^'(.*?)'::/", $column->defaultValue, $matches)) { | |||
$column->defaultValue = $matches[1]; | |||
} elseif (preg_match('/^(.*?)::/', $column->defaultValue, $matches)) { | |||
} elseif (preg_match('/^(?:\()?(.*?)(?(1)\))(?:::.+)?$/', $column->defaultValue, $matches)) { | |||
if ($matches[1] === 'NULL') { | |||
$column->defaultValue = null; | |||
} else { |
@@ -130,7 +130,9 @@ class HttpCache extends ActionFilter | |||
} | |||
if ($this->etagSeed !== null) { | |||
$seed = call_user_func($this->etagSeed, $action, $this->params); | |||
$etag = $this->generateEtag($seed); | |||
if ($seed !== null) { | |||
$etag = $this->generateEtag($seed); | |||
} | |||
} | |||
$this->sendCacheControlHeader(); | |||
@@ -140,20 +142,22 @@ class HttpCache extends ActionFilter | |||
$response->getHeaders()->set('Etag', $etag); | |||
} | |||
if ($this->validateCache($lastModified, $etag)) { | |||
$cacheValid = $this->validateCache($lastModified, $etag); | |||
// https://tools.ietf.org/html/rfc7232#section-4.1 | |||
if ($lastModified !== null && (!$cacheValid || ($cacheValid && $etag === null))) { | |||
$response->getHeaders()->set('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); | |||
} | |||
if ($cacheValid) { | |||
$response->setStatusCode(304); | |||
return false; | |||
} | |||
if ($lastModified !== null) { | |||
$response->getHeaders()->set('Last-Modified', gmdate('D, d M Y H:i:s', $lastModified) . ' GMT'); | |||
} | |||
return true; | |||
} | |||
/** | |||
* Validates if the HTTP cache contains valid content. | |||
* If both Last-Modified and ETag are null, returns false. | |||
* @param integer $lastModified the calculated Last-Modified value in terms of a UNIX timestamp. | |||
* If null, the Last-Modified header will not be validated. | |||
* @param string $etag the calculated ETag value. If null, the ETag header will not be validated. | |||
@@ -168,7 +172,7 @@ class HttpCache extends ActionFilter | |||
} elseif (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { | |||
return $lastModified !== null && @strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified; | |||
} else { | |||
return $etag === null && $lastModified === null; | |||
return false; | |||
} | |||
} | |||
@@ -189,7 +193,6 @@ class HttpCache extends ActionFilter | |||
} | |||
$headers = Yii::$app->getResponse()->getHeaders(); | |||
$headers->set('Pragma'); | |||
if ($this->cacheControlHeader !== null) { | |||
$headers->set('Cache-Control', $this->cacheControlHeader); |
@@ -164,6 +164,7 @@ class PageCache extends ActionFilter | |||
ob_implicit_flush(false); | |||
if ($this->view->beginCache($id, $properties)) { | |||
$response->on(Response::EVENT_AFTER_SEND, [$this, 'cacheResponse']); | |||
Yii::trace('Valid page content is not found in the cache.', __METHOD__); | |||
return true; | |||
} else { | |||
$data = $this->cache->get($this->calculateCacheKey()); | |||
@@ -171,6 +172,7 @@ class PageCache extends ActionFilter | |||
$this->restoreResponse($response, $data); | |||
} | |||
$response->content = ob_get_clean(); | |||
Yii::trace('Valid page content is found in the cache.', __METHOD__); | |||
return false; | |||
} | |||
} |
@@ -39,7 +39,8 @@ abstract class AuthMethod extends ActionFilter implements AuthInterface | |||
* @var array list of action IDs that this filter will be applied to, but auth failure will not lead to error. | |||
* It may be used for actions, that are allowed for public, but return some additional data for authenticated users. | |||
* Defaults to empty, meaning authentication is not optional for any action. | |||
* @see isOptional | |||
* Since version 2.0.10 action IDs can be specified as wildcards, e.g. `site/*`. | |||
* @see isOptional() | |||
* @since 2.0.7 | |||
*/ | |||
public $optional = []; | |||
@@ -87,20 +88,25 @@ abstract class AuthMethod extends ActionFilter implements AuthInterface | |||
*/ | |||
public function handleFailure($response) | |||
{ | |||
throw new UnauthorizedHttpException('You are requesting with an invalid credential.'); | |||
throw new UnauthorizedHttpException('Your request was made with invalid credentials.'); | |||
} | |||
/** | |||
* Checks, whether authentication is optional for the given action. | |||
* | |||
* @param Action $action | |||
* @return boolean | |||
* @param Action $action action to be checked. | |||
* @return boolean whether authentication is optional or not. | |||
* @see optional | |||
* @since 2.0.7 | |||
*/ | |||
protected function isOptional($action) | |||
{ | |||
$id = $this->getActionId($action); | |||
return in_array($id, $this->optional, true); | |||
foreach ($this->optional as $pattern) { | |||
if (fnmatch($pattern, $id)) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
} |
@@ -23,6 +23,30 @@ namespace yii\filters\auth; | |||
* } | |||
* ``` | |||
* | |||
* The default implementation of HttpBasicAuth uses the [[\yii\web\User::loginByAccessToken()|loginByAccessToken()]] | |||
* method of the `user` application component and only passes the user name. This implementation is used | |||
* for authenticating API clients. | |||
* | |||
* If you want to authenticate users using username and password, you should provide the [[auth]] function for example like the following: | |||
* | |||
* ```php | |||
* public function behaviors() | |||
* { | |||
* return [ | |||
* 'basicAuth' => [ | |||
* 'class' => \yii\filters\auth\HttpBasicAuth::className(), | |||
* 'auth' => function ($username, $password) { | |||
* $user = User::find()->where(['username' => $username])->one(); | |||
* if ($user->verifyPassword($password)) { | |||
* return $user; | |||
* } | |||
* return null; | |||
* }, | |||
* ], | |||
* ]; | |||
* } | |||
* ``` | |||
* | |||
* @author Qiang Xue <qiang.xue@gmail.com> | |||
* @since 2.0 | |||
*/ |
@@ -8,7 +8,6 @@ | |||
namespace yii\grid; | |||
use Yii; | |||
use Closure; | |||
use yii\helpers\Html; | |||
use yii\helpers\Url; | |||
@@ -108,7 +107,15 @@ class ActionColumn extends Column | |||
public $visibleButtons = []; | |||
/** | |||
* @var callable a callback that creates a button URL using the specified model information. | |||
* The signature of the callback should be the same as that of [[createUrl()]]. | |||
* The signature of the callback should be the same as that of [[createUrl()]] | |||
* Since 2.0.10 it can accept additional parameter, which refers to the column instance itself: | |||
* | |||
* ```php | |||
* function (string $action, mixed $model, mixed $key, integer $index, ActionColumn $this) { | |||
* //return string; | |||
* } | |||
* ``` | |||
* | |||
* If this property is not set, button URLs will be created using [[createUrl()]]. | |||
*/ | |||
public $urlCreator; | |||
@@ -179,7 +186,7 @@ class ActionColumn extends Column | |||
public function createUrl($action, $model, $key, $index) | |||
{ | |||
if (is_callable($this->urlCreator)) { | |||
return call_user_func($this->urlCreator, $action, $model, $key, $index); | |||
return call_user_func($this->urlCreator, $action, $model, $key, $index, $this); | |||
} else { | |||
$params = is_array($key) ? $key : ['id' => (string) $key]; | |||
$params[0] = $this->controller ? $this->controller . '/' . $action : $action; |
@@ -107,6 +107,8 @@ class BaseArrayHelper | |||
* type and are having the same key. | |||
* For integer-keyed elements, the elements from the latter array will | |||
* be appended to the former array. | |||
* You can use [[UnsetArrayValue]] object to unset value from previous array or | |||
* [[ReplaceArrayValue]] to force replace former value instead of recursive merging. | |||
* @param array $a array to be merged to | |||
* @param array $b array to be merged from. You can specify additional | |||
* arrays via third argument, fourth argument etc. | |||
@@ -119,7 +121,11 @@ class BaseArrayHelper | |||
while (!empty($args)) { | |||
$next = array_shift($args); | |||
foreach ($next as $k => $v) { | |||
if (is_int($k)) { | |||
if ($v instanceof UnsetArrayValue) { | |||
unset($res[$k]); | |||
} elseif ($v instanceof ReplaceArrayValue) { | |||
$res[$k] = $v->value; | |||
} elseif (is_int($k)) { | |||
if (isset($res[$k])) { | |||
$res[] = $v; | |||
} else { | |||
@@ -486,7 +492,9 @@ class BaseArrayHelper | |||
public static function keyExists($key, $array, $caseSensitive = true) | |||
{ | |||
if ($caseSensitive) { | |||
return array_key_exists($key, $array); | |||
// Function `isset` checks key faster but skips `null`, `array_key_exists` handles this case | |||
// http://php.net/manual/en/function.array-key-exists.php#107786 | |||
return isset($array[$key]) || array_key_exists($key, $array); | |||
} else { | |||
foreach (array_keys($array) as $k) { | |||
if (strcasecmp($key, $k) === 0) { | |||
@@ -760,6 +768,7 @@ class BaseArrayHelper | |||
* Filters array according to rules specified. | |||
* | |||
* For example: | |||
* | |||
* ```php | |||
* $array = [ | |||
* 'A' => [1, 2], | |||
@@ -782,7 +791,6 @@ class BaseArrayHelper | |||
* // 'A' => [1, 2], | |||
* // 'B' => ['C' => 1], | |||
* // ] | |||
* ``` | |||
* | |||
* $result = \yii\helpers\ArrayHelper::filter($array, ['B', '!B.C']); | |||
* // $result will be: |
@@ -614,13 +614,13 @@ class BaseConsole | |||
$output = []; | |||
exec('mode con', $output); | |||
if (isset($output, $output[1]) && strpos($output[1], 'CON') !== false) { | |||
return $size = [(int) preg_replace('~\D~', '', $output[3]), (int) preg_replace('~\D~', '', $output[4])]; | |||
return $size = [(int) preg_replace('~\D~', '', $output[4]), (int) preg_replace('~\D~', '', $output[3])]; | |||
} | |||
} else { | |||
// try stty if available | |||
$stty = []; | |||
if (exec('stty -a 2>&1', $stty) && preg_match('/rows\s+(\d+);\s*columns\s+(\d+);/mi', implode(' ', $stty), $matches)) { | |||
return $size = [$matches[2], $matches[1]]; | |||
return $size = [(int)$matches[2], (int)$matches[1]]; | |||
} | |||
// fallback to tput, which may not be updated on terminal resize |
@@ -357,32 +357,34 @@ class BaseFileHelper | |||
* @param string $dir the directory under which the files will be looked for. | |||
* @param array $options options for file searching. Valid options are: | |||
* | |||
* - filter: callback, a PHP callback that is called for each directory or file. | |||
* - `filter`: callback, a PHP callback that is called for each directory or file. | |||
* The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. | |||
* The callback can return one of the following values: | |||
* | |||
* * true: the directory or file will be returned (the "only" and "except" options will be ignored) | |||
* * false: the directory or file will NOT be returned (the "only" and "except" options will be ignored) | |||
* * null: the "only" and "except" options will determine whether the directory or file should be returned | |||
* * `true`: the directory or file will be returned (the `only` and `except` options will be ignored) | |||
* * `false`: the directory or file will NOT be returned (the `only` and `except` options will be ignored) | |||
* * `null`: the `only` and `except` options will determine whether the directory or file should be returned | |||
* | |||
* - except: array, list of patterns excluding from the results matching file or directory paths. | |||
* Patterns ending with '/' apply to directory paths only, and patterns not ending with '/' | |||
* - `except`: array, list of patterns excluding from the results matching file or directory paths. | |||
* Patterns ending with slash ('/') apply to directory paths only, and patterns not ending with '/' | |||
* apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b'; | |||
* and '.svn/' matches directory paths ending with '.svn'. | |||
* If the pattern does not contain a slash /, it is treated as a shell glob pattern and checked for a match against the pathname relative to $dir. | |||
* Otherwise, the pattern is treated as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname. | |||
* For example, "views/*.php" matches "views/index.php" but not "views/controller/index.php". | |||
* A leading slash matches the beginning of the pathname. For example, "/*.php" matches "index.php" but not "views/start/index.php". | |||
* An optional prefix "!" which negates the pattern; any matching file excluded by a previous pattern will become included again. | |||
* If a negated pattern matches, this will override lower precedence patterns sources. Put a backslash ("\") in front of the first "!" | |||
* for patterns that begin with a literal "!", for example, "\!important!.txt". | |||
* and `.svn/` matches directory paths ending with `.svn`. | |||
* If the pattern does not contain a slash (`/`), it is treated as a shell glob pattern | |||
* and checked for a match against the pathname relative to `$dir`. | |||
* Otherwise, the pattern is treated as a shell glob suitable for consumption by `fnmatch(3)` | |||
* `with the `FNM_PATHNAME` flag: wildcards in the pattern will not match a `/` in the pathname. | |||
* For example, `views/*.php` matches `views/index.php` but not `views/controller/index.php`. | |||
* A leading slash matches the beginning of the pathname. For example, `/*.php` matches `index.php` but not `views/start/index.php`. | |||
* An optional prefix `!` which negates the pattern; any matching file excluded by a previous pattern will become included again. | |||
* If a negated pattern matches, this will override lower precedence patterns sources. Put a backslash (`\`) in front of the first `!` | |||
* for patterns that begin with a literal `!`, for example, `\!important!.txt`. | |||
* Note, the '/' characters in a pattern matches both '/' and '\' in the paths. | |||
* - only: array, list of patterns that the file paths should match if they are to be returned. Directory paths are not checked against them. | |||
* Same pattern matching rules as in the "except" option are used. | |||
* If a file path matches a pattern in both "only" and "except", it will NOT be returned. | |||
* - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults to true. | |||
* - recursive: boolean, whether the files under the subdirectories should also be looked for. Defaults to true. | |||
* @return array files found under the directory. The file list is sorted. | |||
* - `only`: array, list of patterns that the file paths should match if they are to be returned. Directory paths | |||
* are not checked against them. Same pattern matching rules as in the `except` option are used. | |||
* If a file path matches a pattern in both `only` and `except`, it will NOT be returned. | |||
* - `caseSensitive`: boolean, whether patterns specified at `only` or `except` should be case sensitive. Defaults to `true`. | |||
* - `recursive`: boolean, whether the files under the subdirectories should also be looked for. Defaults to `true`. | |||
* @return array files found under the directory, in no particular order. Ordering depends on the files system used. | |||
* @throws InvalidParamException if the dir is invalid. | |||
*/ | |||
public static function findFiles($dir, $options = []) |
@@ -743,7 +743,7 @@ class BaseHtml | |||
/** | |||
* Generates a drop-down list. | |||
* @param string $name the input name | |||
* @param string $selection the selected value | |||
* @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s). | |||
* @param array $items the option data items. The array keys are option values, and the array values | |||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). | |||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. | |||
@@ -792,7 +792,7 @@ class BaseHtml | |||
/** | |||
* Generates a list box. | |||
* @param string $name the input name | |||
* @param string|array $selection the selected value(s) | |||
* @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s). | |||
* @param array $items the option data items. The array keys are option values, and the array values | |||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). | |||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. | |||
@@ -858,7 +858,7 @@ class BaseHtml | |||
* A checkbox list allows multiple selection, like [[listBox()]]. | |||
* As a result, the corresponding submitted value is an array. | |||
* @param string $name the name attribute of each checkbox. | |||
* @param string|array $selection the selected value(s). | |||
* @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s). | |||
* @param array $items the data item used to generate the checkboxes. | |||
* The array keys are the checkbox values, while the array values are the corresponding labels. | |||
* @param array $options options (name => config) for the checkbox list container tag. | |||
@@ -938,7 +938,7 @@ class BaseHtml | |||
* Generates a list of radio buttons. | |||
* A radio button list is like a checkbox list, except that it only allows single selection. | |||
* @param string $name the name attribute of each radio button. | |||
* @param string|array $selection the selected value(s). | |||
* @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s). | |||
* @param array $items the data item used to generate the radio buttons. | |||
* The array keys are the radio button values, while the array values are the corresponding labels. | |||
* @param array $options options (name => config) for the radio button list container tag. | |||
@@ -1148,15 +1148,18 @@ class BaseHtml | |||
/** | |||
* Generates a summary of the validation errors. | |||
* If there is no validation error, an empty error summary markup will still be generated, but it will be hidden. | |||
* @param Model|Model[] $models the model(s) whose validation errors are to be displayed | |||
* @param Model|Model[] $models the model(s) whose validation errors are to be displayed. | |||
* @param array $options the tag options in terms of name-value pairs. The following options are specially handled: | |||
* | |||
* - header: string, the header HTML for the error summary. If not set, a default prompt string will be used. | |||
* - footer: string, the footer HTML for the error summary. | |||
* - encode: boolean, if set to false then the error messages won't be encoded. | |||
* - footer: string, the footer HTML for the error summary. Defaults to empty string. | |||
* - encode: boolean, if set to false then the error messages won't be encoded. Defaults to `true`. | |||
* - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise | |||
* only the first error message for each attribute will be shown. Defaults to `false`. | |||
* Option is available since 2.0.10. | |||
* | |||
* The rest of the options will be rendered as the attributes of the container tag. | |||
* | |||
* The rest of the options will be rendered as the attributes of the container tag. The values will | |||
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. | |||
* @return string the generated error summary | |||
*/ | |||
public static function errorSummary($models, $options = []) | |||
@@ -1164,6 +1167,7 @@ class BaseHtml | |||
$header = isset($options['header']) ? $options['header'] : '<p>' . Yii::t('yii', 'Please fix the following errors:') . '</p>'; | |||
$footer = ArrayHelper::remove($options, 'footer', ''); | |||
$encode = ArrayHelper::remove($options, 'encode', true); | |||
$showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false); | |||
unset($options['header']); | |||
$lines = []; | |||
@@ -1172,8 +1176,16 @@ class BaseHtml | |||
} | |||
foreach ($models as $model) { | |||
/* @var $model Model */ | |||
foreach ($model->getFirstErrors() as $error) { | |||
$lines[] = $encode ? Html::encode($error) : $error; | |||
foreach ($model->getErrors() as $errors) { | |||
foreach ($errors as $error) { | |||
$line = $encode ? Html::encode($error) : $error; | |||
if (array_search($line, $lines) === false) { | |||
$lines[] = $line; | |||
} | |||
if (!$showAllErrors) { | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
@@ -1648,7 +1660,7 @@ class BaseHtml | |||
protected static function activeListInput($type, $model, $attribute, $items, $options = []) | |||
{ | |||
$name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute); | |||
$selection = static::getAttributeValue($model, $attribute); | |||
$selection = isset($options['value']) ? $options['value'] : static::getAttributeValue($model, $attribute); | |||
if (!array_key_exists('unselect', $options)) { | |||
$options['unselect'] = ''; | |||
} | |||
@@ -1660,8 +1672,7 @@ class BaseHtml | |||
/** | |||
* Renders the option tags that can be used by [[dropDownList()]] and [[listBox()]]. | |||
* @param string|array $selection the selected value(s). This can be either a string for single selection | |||
* or an array for multiple selections. | |||
* @param string|array|null $selection the selected value(s). String for single or array for multiple selection(s). | |||
* @param array $items the option data items. The array keys are option values, and the array values | |||
* are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). | |||
* For each sub-array, an option group will be generated whose label is the key associated with the sub-array. |
@@ -142,7 +142,7 @@ class BaseJson | |||
return $token; | |||
} elseif ($data instanceof \JsonSerializable) { | |||
$data = $data->jsonSerialize(); | |||
return static::processData($data->jsonSerialize(), $expressions, $expPrefix); | |||
} elseif ($data instanceof Arrayable) { | |||
$data = $data->toArray(); | |||
} elseif ($data instanceof \SimpleXMLElement) { |
@@ -109,7 +109,7 @@ class BaseStringHelper | |||
} | |||
if (mb_strlen($string, $encoding ?: Yii::$app->charset) > $length) { | |||
return trim(mb_substr($string, 0, $length, $encoding ?: Yii::$app->charset)) . $suffix; | |||
return rtrim(mb_substr($string, 0, $length, $encoding ?: Yii::$app->charset)) . $suffix; | |||
} else { | |||
return $string; | |||
} | |||
@@ -164,16 +164,14 @@ class BaseStringHelper | |||
$truncated[] = $token; | |||
} elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $count) { //Text | |||
if (false === $encoding) { | |||
$token->data = self::truncateWords($token->data, $count - $totalCount, ''); | |||
preg_match('/^(\s*)/um', $token->data, $prefixSpace) ?: $prefixSpace = ['','']; | |||
$token->data = $prefixSpace[1] . self::truncateWords(ltrim($token->data), $count - $totalCount, ''); | |||
$currentCount = self::countWords($token->data); | |||
} else { | |||
$token->data = self::truncate($token->data, $count - $totalCount, '', $encoding) . ' '; | |||
$token->data = self::truncate($token->data, $count - $totalCount, '', $encoding); | |||
$currentCount = mb_strlen($token->data, $encoding); | |||
} | |||
$totalCount += $currentCount; | |||
if (1 === $currentCount) { | |||
$token->data = ' ' . $token->data; | |||
} | |||
$truncated[] = $token; | |||
} elseif ($token instanceof \HTMLPurifier_Token_End) { //Tag ends | |||
$openTokens--; |
@@ -282,7 +282,7 @@ class BaseUrl | |||
* | |||
* @param string $name the named associated with the URL that was remembered previously. | |||
* If not set, it will use [[\yii\web\User::returnUrlParam]]. | |||
* @return string the URL previously remembered. Null is returned if no URL was remembered with the given name. | |||
* @return string|null the URL previously remembered. Null is returned if no URL was remembered with the given name. | |||
* @see remember() | |||
*/ | |||
public static function previous($name = null) | |||
@@ -371,6 +371,17 @@ class BaseUrl | |||
* echo Url::current(['id' => 100]); | |||
* ``` | |||
* | |||
* Note that if you're replacing array parameters with `[]` at the end you should specify `$params` as nested arrays. | |||
* For a `PostSearchForm` model where parameter names are `PostSearchForm[id]` and `PostSearchForm[src]` the syntax | |||
* would be the following: | |||
* | |||
* ```php | |||
* // index.php?r=post%2Findex&PostSearchForm%5Bid%5D=100&PostSearchForm%5Bsrc%5D=google | |||
* echo Url::current([ | |||
* $postSearch->formName() => ['id' => 100, 'src' => 'google'], | |||
* ]); | |||
* ``` | |||
* | |||
* @param array $params an associative array of parameters that will be merged with the current GET parameters. | |||
* If a parameter value is null, the corresponding GET parameter will be removed. | |||
* @param boolean|string $scheme the URI scheme to use in the generated URL: |