From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001
From: ctrlaltca <>
Date: Thu, 12 Jul 2012 11:21:01 +0000
Subject: standardize the use of unix eol; use svn properties to enforce native
eol
---
framework/3rdParty/PhpShell/PHP/Shell.php | 2182 ++++-----
framework/3rdParty/PhpShell/php-shell-init.php | 174 +-
framework/3rdParty/SafeHtml/HTMLSax3.php | 1388 +++---
.../3rdParty/SafeHtml/HTMLSax3/Decorators.php | 724 +--
framework/3rdParty/SafeHtml/HTMLSax3/States.php | 574 +--
framework/3rdParty/SafeHtml/TSafeHtmlParser.php | 1344 +++---
.../3rdParty/TextHighlighter/Text/Highlighter.php | 792 ++--
.../TextHighlighter/Text/Highlighter/ABAP.php | 98 +-
.../TextHighlighter/Text/Highlighter/CPP.php | 90 +-
.../TextHighlighter/Text/Highlighter/CSS.php | 90 +-
.../TextHighlighter/Text/Highlighter/DIFF.php | 90 +-
.../TextHighlighter/Text/Highlighter/DTD.php | 90 +-
.../TextHighlighter/Text/Highlighter/Generator.php | 2508 +++++-----
.../TextHighlighter/Text/Highlighter/HTML.php | 98 +-
.../TextHighlighter/Text/Highlighter/JAVA.php | 90 +-
.../Text/Highlighter/JAVASCRIPT.php | 90 +-
.../TextHighlighter/Text/Highlighter/MYSQL.php | 90 +-
.../TextHighlighter/Text/Highlighter/PERL.php | 90 +-
.../TextHighlighter/Text/Highlighter/PHP.php | 2176 ++++-----
.../TextHighlighter/Text/Highlighter/PRADO.php | 506 +--
.../TextHighlighter/Text/Highlighter/PYTHON.php | 90 +-
.../TextHighlighter/Text/Highlighter/RUBY.php | 90 +-
.../TextHighlighter/Text/Highlighter/Renderer.php | 302 +-
.../Text/Highlighter/Renderer/Array.php | 396 +-
.../Text/Highlighter/Renderer/BB.php | 474 +-
.../Text/Highlighter/Renderer/Console.php | 414 +-
.../Text/Highlighter/Renderer/Html.php | 890 ++--
.../Text/Highlighter/Renderer/HtmlTags.php | 372 +-
.../Text/Highlighter/Renderer/JSON.php | 170 +-
.../Text/Highlighter/Renderer/XML.php | 204 +-
.../TextHighlighter/Text/Highlighter/SQL.php | 90 +-
.../TextHighlighter/Text/Highlighter/XML.php | 90 +-
framework/Caching/TAPCCache.php | 266 +-
framework/Caching/TCache.php | 1440 +++---
framework/Caching/TDbCache.php | 1156 ++---
framework/Caching/TSqliteCache.php | 448 +-
framework/Caching/TXCache.php | 262 +-
framework/Collections/TAttributeCollection.php | 342 +-
framework/Collections/TDummyDataSource.php | 290 +-
framework/Collections/TList.php | 972 ++--
framework/Collections/TListItemCollection.php | 328 +-
framework/Collections/TMap.php | 704 +--
framework/Collections/TPagedDataSource.php | 890 ++--
framework/Collections/TPagedList.php | 952 ++--
framework/Collections/TPriorityList.php | 1486 +++---
framework/Collections/TQueue.php | 526 +--
framework/Collections/TStack.php | 524 +--
.../Exceptions/TActiveRecordException.php | 98 +-
.../Relations/TActiveRecordBelongsTo.php | 274 +-
.../Relations/TActiveRecordHasMany.php | 240 +-
.../Relations/TActiveRecordHasManyAssociation.php | 750 +--
.../ActiveRecord/Relations/TActiveRecordHasOne.php | 288 +-
.../Relations/TActiveRecordRelation.php | 508 +--
.../Relations/TActiveRecordRelationContext.php | 458 +-
.../Data/ActiveRecord/Scaffold/TScaffoldBase.php | 414 +-
.../ActiveRecord/Scaffold/TScaffoldEditView.php | 616 +--
.../ActiveRecord/Scaffold/TScaffoldListView.php | 608 +--
.../Data/ActiveRecord/Scaffold/TScaffoldSearch.php | 296 +-
.../Data/ActiveRecord/Scaffold/TScaffoldView.php | 286 +-
.../Data/ActiveRecord/TActiveRecordConfig.php | 400 +-
.../Data/ActiveRecord/TActiveRecordCriteria.php | 72 +-
.../Data/ActiveRecord/TActiveRecordManager.php | 324 +-
.../Data/Common/Mssql/TMssqlCommandBuilder.php | 346 +-
framework/Data/Common/Mssql/TMssqlTableColumn.php | 126 +-
framework/Data/Common/Mssql/TMssqlTableInfo.php | 126 +-
.../Data/Common/Mysql/TMysqlCommandBuilder.php | 50 +-
framework/Data/Common/Mysql/TMysqlMetaData.php | 772 ++--
framework/Data/Common/Mysql/TMysqlTableColumn.php | 142 +-
framework/Data/Common/Mysql/TMysqlTableInfo.php | 116 +-
.../Data/Common/Pgsql/TPgsqlCommandBuilder.php | 136 +-
framework/Data/Common/Pgsql/TPgsqlMetaData.php | 844 ++--
framework/Data/Common/Pgsql/TPgsqlTableColumn.php | 92 +-
framework/Data/Common/Pgsql/TPgsqlTableInfo.php | 110 +-
.../Data/Common/Sqlite/TSqliteCommandBuilder.php | 92 +-
framework/Data/Common/Sqlite/TSqliteMetaData.php | 420 +-
.../Data/Common/Sqlite/TSqliteTableColumn.php | 126 +-
framework/Data/Common/Sqlite/TSqliteTableInfo.php | 92 +-
framework/Data/Common/TDbCommandBuilder.php | 1014 ++---
framework/Data/Common/TDbMetaData.php | 364 +-
framework/Data/Common/TDbTableColumn.php | 396 +-
framework/Data/Common/TDbTableInfo.php | 310 +-
framework/Data/DataGateway/TDataGatewayCommand.php | 1082 ++---
framework/Data/DataGateway/TSqlCriteria.php | 566 +--
framework/Data/DataGateway/TTableGateway.php | 948 ++--
.../Data/SqlMap/Configuration/TDiscriminator.php | 462 +-
.../Configuration/TInlineParameterMapParser.php | 156 +-
.../Data/SqlMap/Configuration/TParameterMap.php | 420 +-
.../SqlMap/Configuration/TParameterProperty.php | 298 +-
framework/Data/SqlMap/Configuration/TResultMap.php | 400 +-
.../Data/SqlMap/Configuration/TResultProperty.php | 688 +--
.../SqlMap/Configuration/TSimpleDynamicParser.php | 88 +-
.../SqlMap/Configuration/TSqlMapCacheModel.php | 492 +-
.../Data/SqlMap/Configuration/TSqlMapStatement.php | 902 ++--
.../Configuration/TSqlMapXmlConfiguration.php | 1610 +++----
.../DataMapper/TFastSqlMapApplicationCache.php | 178 +-
framework/Data/SqlMap/DataMapper/TLazyLoadList.php | 286 +-
.../Data/SqlMap/DataMapper/TPropertyAccess.php | 312 +-
framework/Data/SqlMap/DataMapper/TSqlMapCache.php | 590 +--
.../Data/SqlMap/DataMapper/TSqlMapException.php | 230 +-
.../Data/SqlMap/DataMapper/TSqlMapPagedList.php | 416 +-
.../DataMapper/TSqlMapTypeHandlerRegistry.php | 382 +-
.../Data/SqlMap/Statements/IMappedStatement.php | 164 +-
.../Data/SqlMap/Statements/TCachingStatement.php | 216 +-
.../SqlMap/Statements/TDeleteMappedStatement.php | 46 +-
.../SqlMap/Statements/TInsertMappedStatement.php | 96 +-
.../Data/SqlMap/Statements/TMappedStatement.php | 2484 +++++-----
.../Data/SqlMap/Statements/TPreparedCommand.php | 132 +-
.../Data/SqlMap/Statements/TPreparedStatement.php | 112 +-
.../Statements/TPreparedStatementFactory.php | 98 +-
.../SqlMap/Statements/TSelectMappedStatement.php | 70 +-
.../Data/SqlMap/Statements/TSimpleDynamicSql.php | 78 +-
framework/Data/SqlMap/Statements/TStaticSql.php | 70 +-
.../SqlMap/Statements/TUpdateMappedStatement.php | 96 +-
framework/Data/SqlMap/TSqlMapConfig.php | 360 +-
framework/Data/SqlMap/TSqlMapGateway.php | 516 +--
framework/Data/SqlMap/TSqlMapManager.php | 546 +--
framework/Data/TDataSourceConfig.php | 334 +-
framework/Data/TDbCommand.php | 616 +--
framework/Data/TDbConnection.php | 1366 +++---
framework/Data/TDbDataReader.php | 448 +-
framework/Data/TDbTransaction.php | 222 +-
framework/Exceptions/TErrorHandler.php | 826 ++--
framework/Exceptions/TException.php | 832 ++--
framework/I18N/TChoiceFormat.php | 220 +-
framework/I18N/TDateFormat.php | 506 +--
framework/I18N/TGlobalization.php | 598 +--
framework/I18N/TGlobalizationAutoDetect.php | 96 +-
framework/I18N/TI18NControl.php | 180 +-
framework/I18N/TNumberFormat.php | 502 +-
framework/I18N/TTranslate.php | 510 +--
framework/I18N/TTranslateParameter.php | 236 +-
framework/I18N/core/ChoiceFormat.php | 452 +-
framework/I18N/core/CultureInfo.php | 1264 +++---
framework/I18N/core/DateFormat.php | 1304 +++---
framework/I18N/core/DateTimeFormatInfo.php | 1032 ++---
framework/I18N/core/Gettext/MO.php | 710 +--
framework/I18N/core/Gettext/PO.php | 320 +-
framework/I18N/core/Gettext/TGettext.php | 572 +--
framework/I18N/core/HTTPNegotiator.php | 258 +-
framework/I18N/core/IMessageSource.php | 244 +-
framework/I18N/core/MessageCache.php | 342 +-
framework/I18N/core/MessageFormat.php | 510 +--
framework/I18N/core/MessageSource.php | 672 +--
framework/I18N/core/MessageSource_Database.php | 646 +--
framework/I18N/core/MessageSource_MySQL.php | 836 ++--
framework/I18N/core/MessageSource_SQLite.php | 708 +--
framework/I18N/core/MessageSource_XLIFF.php | 1058 ++---
framework/I18N/core/MessageSource_gettext.php | 914 ++--
framework/I18N/core/NumberFormat.php | 612 +--
framework/I18N/core/NumberFormatInfo.php | 1300 +++---
framework/I18N/core/TCache_Lite.php | 1258 ++---
framework/I18N/core/util.php | 374 +-
framework/IO/TTarFileExtractor.php | 1146 ++---
framework/IO/TTextWriter.php | 116 +-
framework/Security/IUserManager.php | 114 +-
framework/Security/TAuthManager.php | 914 ++--
framework/Security/TAuthorizationRule.php | 590 +--
framework/Security/TDbUserManager.php | 638 +--
framework/Security/TUser.php | 442 +-
framework/Security/TUserManager.php | 802 ++--
framework/TApplicationComponent.php | 234 +-
framework/TModule.php | 110 +-
framework/TShellApplication.php | 96 +-
framework/Util/TDataFieldAccessor.php | 164 +-
framework/Util/TDateTimeStamp.php | 1406 +++---
framework/Util/TLogRouter.php | 2414 +++++-----
framework/Util/TLogger.php | 472 +-
framework/Util/TParameterModule.php | 346 +-
framework/Util/TSimpleDateFormatter.php | 752 +--
framework/Util/TVarDumper.php | 254 +-
framework/Web/Javascripts/JSMin.php | 580 +--
framework/Web/Javascripts/TJavaScript.php | 566 +--
framework/Web/Javascripts/packages.php | 242 +-
.../source/prado/activecontrols/activecontrols3.js | 820 ++--
.../prado/activecontrols/activedatepicker.js | 174 +-
.../source/prado/activecontrols/ajax3.js | 2288 +++++-----
.../source/prado/activecontrols/dragdrop.js | 122 +-
.../source/prado/activecontrols/inlineeditor.js | 602 +--
.../source/prado/colorpicker/colorpicker.js | 1560 +++----
.../Javascripts/source/prado/controls/controls.js | 1034 ++---
.../Javascripts/source/prado/controls/htmlarea.js | 298 +-
.../Javascripts/source/prado/controls/keyboard.js | 322 +-
.../Javascripts/source/prado/controls/tabpanel.js | 120 +-
.../source/prado/datepicker/datepicker.js | 1578 +++----
.../Web/Javascripts/source/prado/logger/logger.js | 1506 +++---
framework/Web/Javascripts/source/prado/prado.js | 188 +-
.../Javascripts/source/prado/ratings/ratings.js | 412 +-
.../source/prado/scriptaculous-adapter.js | 2792 ++++++------
.../source/prado/validator/validation3.js | 3886 ++++++++--------
framework/Web/Services/TFeedService.php | 374 +-
framework/Web/Services/TJsonService.php | 426 +-
framework/Web/Services/TPageService.php | 1786 ++++----
framework/Web/TAssetManager.php | 714 +--
framework/Web/THttpResponse.php | 1438 +++---
framework/Web/THttpSession.php | 1460 +++---
framework/Web/THttpUtility.php | 124 +-
framework/Web/TUrlManager.php | 280 +-
framework/Web/TUrlMapping.php | 1768 ++++----
framework/Web/UI/ActiveControls/TActiveButton.php | 264 +-
.../Web/UI/ActiveControls/TActiveClientScript.php | 164 +-
.../UI/ActiveControls/TActiveControlAdapter.php | 1150 ++---
.../UI/ActiveControls/TActiveCustomValidator.php | 530 +--
framework/Web/UI/ActiveControls/TActiveLabel.php | 2 +-
.../Web/UI/ActiveControls/TActivePageAdapter.php | 802 ++--
framework/Web/UI/ActiveControls/TActivePanel.php | 200 +-
.../Web/UI/ActiveControls/TActiveRatingList.php | 266 +-
framework/Web/UI/ActiveControls/TActiveTextBox.php | 250 +-
framework/Web/UI/ActiveControls/TAutoComplete.php | 880 ++--
.../Web/UI/ActiveControls/TBaseActiveControl.php | 784 ++--
framework/Web/UI/ActiveControls/TCallback.php | 202 +-
.../UI/ActiveControls/TCallbackClientScript.php | 1410 +++---
.../Web/UI/ActiveControls/TCallbackClientSide.php | 644 +--
.../UI/ActiveControls/TCallbackEventParameter.php | 174 +-
.../Web/UI/ActiveControls/TCallbackOptions.php | 106 +-
.../UI/ActiveControls/TEventTriggeredCallback.php | 190 +-
.../Web/UI/ActiveControls/TInPlaceTextBox.php | 580 +--
.../Web/UI/ActiveControls/TTriggeredCallback.php | 142 +-
.../UI/ActiveControls/TValueTriggeredCallback.php | 240 +-
framework/Web/UI/TCachePageStatePersister.php | 400 +-
framework/Web/UI/TCompositeControl.php | 74 +-
framework/Web/UI/TControl.php | 4790 ++++++++++----------
framework/Web/UI/TControlAdapter.php | 286 +-
framework/Web/UI/TForm.php | 342 +-
framework/Web/UI/THtmlWriter.php | 458 +-
framework/Web/UI/TPage.php | 2682 +++++------
framework/Web/UI/TPageStatePersister.php | 140 +-
framework/Web/UI/TSessionPageStatePersister.php | 260 +-
framework/Web/UI/TTemplateControl.php | 484 +-
framework/Web/UI/TTemplateManager.php | 2150 ++++-----
framework/Web/UI/TThemeManager.php | 1034 ++---
framework/Web/UI/WebControls/TAccordion.php | 1488 +++---
framework/Web/UI/WebControls/TBaseDataList.php | 378 +-
framework/Web/UI/WebControls/TBoundColumn.php | 498 +-
framework/Web/UI/WebControls/TBulletedList.php | 982 ++--
framework/Web/UI/WebControls/TButton.php | 734 +--
framework/Web/UI/WebControls/TButtonColumn.php | 554 +--
framework/Web/UI/WebControls/TCaptcha.php | 990 ++--
framework/Web/UI/WebControls/TCaptchaValidator.php | 254 +-
framework/Web/UI/WebControls/TCheckBox.php | 1026 ++---
framework/Web/UI/WebControls/TCheckBoxColumn.php | 244 +-
framework/Web/UI/WebControls/TCheckBoxList.php | 998 ++--
framework/Web/UI/WebControls/TColorPicker.php | 580 +--
framework/Web/UI/WebControls/TCompareValidator.php | 528 +--
framework/Web/UI/WebControls/TConditional.php | 284 +-
framework/Web/UI/WebControls/TContent.php | 92 +-
.../Web/UI/WebControls/TContentPlaceHolder.php | 94 +-
framework/Web/UI/WebControls/TCustomValidator.php | 414 +-
framework/Web/UI/WebControls/TDataBoundControl.php | 1174 ++---
framework/Web/UI/WebControls/TDataGrid.php | 4516 +++++++++---------
framework/Web/UI/WebControls/TDataGridColumn.php | 1134 ++---
.../Web/UI/WebControls/TDataGridItemRenderer.php | 58 +-
.../Web/UI/WebControls/TDataGridPagerStyle.php | 510 +--
framework/Web/UI/WebControls/TDataList.php | 3530 +++++++--------
.../Web/UI/WebControls/TDataListItemRenderer.php | 342 +-
framework/Web/UI/WebControls/TDataRenderer.php | 102 +-
.../Web/UI/WebControls/TDataSourceControl.php | 234 +-
framework/Web/UI/WebControls/TDataSourceView.php | 410 +-
.../Web/UI/WebControls/TDataTypeValidator.php | 280 +-
framework/Web/UI/WebControls/TDatePicker.php | 1986 ++++----
framework/Web/UI/WebControls/TDropDownList.php | 308 +-
.../Web/UI/WebControls/TDropDownListColumn.php | 640 +--
.../Web/UI/WebControls/TEditCommandColumn.php | 528 +--
.../Web/UI/WebControls/TEmailAddressValidator.php | 192 +-
framework/Web/UI/WebControls/TExpression.php | 122 +-
framework/Web/UI/WebControls/TFileUpload.php | 562 +--
framework/Web/UI/WebControls/TFlushOutput.php | 170 +-
framework/Web/UI/WebControls/TFont.php | 634 +--
framework/Web/UI/WebControls/THead.php | 754 +--
framework/Web/UI/WebControls/THiddenField.php | 410 +-
framework/Web/UI/WebControls/THtmlElement.php | 136 +-
framework/Web/UI/WebControls/THyperLink.php | 454 +-
framework/Web/UI/WebControls/THyperLinkColumn.php | 546 +--
framework/Web/UI/WebControls/TImage.php | 312 +-
framework/Web/UI/WebControls/TImageButton.php | 882 ++--
framework/Web/UI/WebControls/TImageMap.php | 1672 +++----
framework/Web/UI/WebControls/TItemDataRenderer.php | 164 +-
framework/Web/UI/WebControls/TJavascriptLogger.php | 186 +-
framework/Web/UI/WebControls/TKeyboard.php | 378 +-
framework/Web/UI/WebControls/TLabel.php | 306 +-
framework/Web/UI/WebControls/TLinkButton.php | 666 +--
framework/Web/UI/WebControls/TListBox.php | 486 +-
framework/Web/UI/WebControls/TListControl.php | 1846 ++++----
.../Web/UI/WebControls/TListControlValidator.php | 448 +-
framework/Web/UI/WebControls/TListItem.php | 366 +-
framework/Web/UI/WebControls/TLiteral.php | 222 +-
framework/Web/UI/WebControls/TLiteralColumn.php | 306 +-
framework/Web/UI/WebControls/TMarkdown.php | 148 +-
framework/Web/UI/WebControls/TMultiView.php | 756 +--
framework/Web/UI/WebControls/TOutputCache.php | 1240 ++---
framework/Web/UI/WebControls/TPager.php | 1582 +++----
framework/Web/UI/WebControls/TPanel.php | 492 +-
framework/Web/UI/WebControls/TPanelStyle.php | 554 +--
framework/Web/UI/WebControls/TPlaceHolder.php | 54 +-
framework/Web/UI/WebControls/TRadioButton.php | 638 +--
framework/Web/UI/WebControls/TRangeValidator.php | 716 +--
framework/Web/UI/WebControls/TRatingList.php | 718 +--
framework/Web/UI/WebControls/TReCaptcha.php | 464 +-
.../Web/UI/WebControls/TReCaptchaValidator.php | 244 +-
.../UI/WebControls/TRegularExpressionValidator.php | 288 +-
framework/Web/UI/WebControls/TRepeatInfo.php | 1118 ++---
framework/Web/UI/WebControls/TRepeater.php | 2048 ++++-----
.../Web/UI/WebControls/TRepeaterItemRenderer.php | 98 +-
.../Web/UI/WebControls/TRequiredFieldValidator.php | 274 +-
framework/Web/UI/WebControls/TSafeHtml.php | 170 +-
framework/Web/UI/WebControls/TSlider.php | 1148 ++---
framework/Web/UI/WebControls/TStatements.php | 124 +-
framework/Web/UI/WebControls/TStyle.php | 1786 ++++----
framework/Web/UI/WebControls/TTable.php | 818 ++--
framework/Web/UI/WebControls/TTableCell.php | 442 +-
framework/Web/UI/WebControls/TTableFooterRow.php | 92 +-
framework/Web/UI/WebControls/TTableHeaderCell.php | 246 +-
framework/Web/UI/WebControls/TTableHeaderRow.php | 92 +-
framework/Web/UI/WebControls/TTableRow.php | 414 +-
framework/Web/UI/WebControls/TTemplateColumn.php | 510 +--
framework/Web/UI/WebControls/TTextBox.php | 1304 +++---
framework/Web/UI/WebControls/TTextProcessor.php | 170 +-
.../Web/UI/WebControls/TValidationSummary.php | 1072 ++---
.../Web/UI/WebControls/TWebControlAdapter.php | 140 +-
framework/Web/UI/WebControls/TWizard.php | 4290 +++++++++---------
.../WebControls/TWizardNavigationButtonStyle.php | 308 +-
framework/Web/UI/WebControls/assets/captcha.php | 448 +-
framework/interfaces.php | 760 ++--
framework/prado.php | 132 +-
323 files changed, 100892 insertions(+), 100892 deletions(-)
(limited to 'framework')
diff --git a/framework/3rdParty/PhpShell/PHP/Shell.php b/framework/3rdParty/PhpShell/PHP/Shell.php
index bf8c86c3..8012475e 100644
--- a/framework/3rdParty/PhpShell/PHP/Shell.php
+++ b/framework/3rdParty/PhpShell/PHP/Shell.php
@@ -1,1091 +1,1091 @@
-
-
-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.
-*/
-
-/**
-* A interactive PHP Shell
-*
-* The more I work with other languages like python and ruby I like their way how they
-* work on problems. While PHP is very forgiving on errors, it is weak on the debugging
-* side. It was missing a simple to use interactive shell for years. Python and Ruby have
-* their ipython and iruby shell which give you a direct way to interact with the objects.
-* No need to write a script and execute it afterwards.
-*
-* Starting the Shell:
-*
-* The package contains a shell wrapper for windows and unix:
-*
-* sh> php-shell.sh
-* win> php-shell
-*
-*
-* Both are calling the wrapper script php -q php-shell-cmd.php
-*
-* Inline Help
-*
-*
-* PHP-Shell - Version 0.2.0, with readline() support
-* (c) 2006, Jan Kneschke
-*
-* >> use '?' to open the inline help
-*
-* >> ?
-* "inline help for the PHP-shell
-*
-* >> ?
-* print this help
-* >> ?
-* get the doccomment for a class, method, property or function
-* >> p
-* execute a verbose print (if implemented)
-* >> quit
-* leave shell
-* "
-* >> ? PHP_Shell
-*
-* Alternatives
-*
-* - http://david.acz.org/phpa/
-* - http://www.hping.org/phpinteractive/
-* - the embedded interactive php-shell: $ php -a
-*
-* @package PHP
-*/
-
-/**
-* PHP_Shell
-*
-* a interactive PHP Shell with tab-completion and history
-* it can catch FATAL errors before executing the code
-*
-* Extensions are provided through three side-classes:
-*
-* - PHP_Shell_Commands
-* - PHP_Shell_Options
-* - PHP_Shell_Extensions
-*
-* @package PHP
-*/
-
-require_once(dirname(__FILE__)."/Shell/Commands.php");
-require_once(dirname(__FILE__)."/Shell/Options.php"); /* for the tab-complete */
-
-class PHP_Shell {
- /**
- * current code-buffer
- * @var string
- */
- protected $code;
-
- /**
- * set if readline support is enabled
- * @var bool
- */
- protected $have_readline;
-
- /**
- * current version of the class
- * @var string
- */
- protected $version = '0.3.1';
-
- /**
- *
- */
- protected $stdin;
-
- protected $code_buffer;
-
- public $has_semicolon=false;
-
- /**
- * init the shell and change if readline support is available
- */
- public function __construct() {
- $this->code = '';
-
- $this->stdin = null;
-
- $this->have_readline = function_exists('readline');
-
- if ($this->have_readline) {
- readline_completion_function('__shell_readline_complete');
- }
-
- $this->use_readline = true;
-
- $cmd = PHP_Shell_Commands::getInstance();
-
- $cmd->registerCommand('#^quit$#', $this, 'cmdQuit', 'quit', 'leaves the shell');
- $cmd->registerCommand('#^\?$#', $this, 'cmdHelp', '?', 'show this help');
- $cmd->registerCommand('#^\?\s+license$#', $this, 'cmdLicense', '? license', 'show license of the shell');
- }
-
-
- /**
- * parse the PHP code
- *
- * we parse before we eval() the code to
- * - fetch fatal errors before they come up
- * - know about where we have to wait for closing braces
- *
- * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise
- */
- public function parse() {
- ## remove empty lines
- if (trim($this->code) == '') return 1;
-
- $t = token_get_all('code.' ?>');
-
- $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */
- $need_return = 1; /* can we prepend a return to the eval-string ? */
- $open_comment = 0; /* a open multi-line comment */
- $eval = ''; /* code to be eval()'ed later */
- $braces = array(); /* to track if we need more closing braces */
-
- $methods = array(); /* to track duplicate methods in a class declaration */
- $ts = array(); /* tokens without whitespaces */
-
- foreach ($t as $ndx => $token) {
- if (is_array($token)) {
- $ignore = 0;
-
- switch($token[0]) {
- case T_WHITESPACE:
- case T_OPEN_TAG:
- case T_CLOSE_TAG:
- $ignore = 1;
- break;
- case T_FOREACH:
- case T_DO:
- case T_WHILE:
- case T_FOR:
-
- case T_IF:
- case T_RETURN:
-
- case T_CLASS:
- case T_FUNCTION:
- case T_INTERFACE:
-
- case T_PRINT:
- case T_ECHO:
-
- case T_COMMENT:
- case T_UNSET:
-
- case T_INCLUDE:
- case T_REQUIRE:
- case T_INCLUDE_ONCE:
- case T_REQUIRE_ONCE:
- case T_TRY:
- case T_SWITCH:
- case T_DEFAULT:
- case T_CASE:
- case T_BREAK:
- case T_DOC_COMMENT:
- $need_return = 0;
- break;
- case T_EMPTY:
- case T_ISSET:
- case T_EVAL:
- case T_EXIT:
-
- case T_VARIABLE:
- case T_STRING:
- case T_NEW:
- case T_EXTENDS:
- case T_IMPLEMENTS:
- case T_OBJECT_OPERATOR:
- case T_DOUBLE_COLON:
- case T_INSTANCEOF:
-
- case T_CATCH:
- case T_THROW:
-
- case T_ELSE:
- case T_AS:
- case T_LNUMBER:
- case T_DNUMBER:
- case T_CONSTANT_ENCAPSED_STRING:
- case T_ENCAPSED_AND_WHITESPACE:
- case T_CHARACTER:
- case T_ARRAY:
- case T_DOUBLE_ARROW:
-
- case T_CONST:
- case T_PUBLIC:
- case T_PROTECTED:
- case T_PRIVATE:
- case T_ABSTRACT:
- case T_STATIC:
- case T_VAR:
-
- case T_INC:
- case T_DEC:
- case T_SL:
- case T_SL_EQUAL:
- case T_SR:
- case T_SR_EQUAL:
-
- case T_IS_EQUAL:
- case T_IS_IDENTICAL:
- case T_IS_GREATER_OR_EQUAL:
- case T_IS_SMALLER_OR_EQUAL:
-
- case T_BOOLEAN_OR:
- case T_LOGICAL_OR:
- case T_BOOLEAN_AND:
- case T_LOGICAL_AND:
- case T_LOGICAL_XOR:
- case T_MINUS_EQUAL:
- case T_PLUS_EQUAL:
- case T_MUL_EQUAL:
- case T_DIV_EQUAL:
- case T_MOD_EQUAL:
- case T_XOR_EQUAL:
- case T_AND_EQUAL:
- case T_OR_EQUAL:
-
- case T_FUNC_C:
- case T_CLASS_C:
- case T_LINE:
- case T_FILE:
-
- case T_BOOL_CAST:
- case T_INT_CAST:
- case T_STRING_CAST:
-
- /* just go on */
- break;
- default:
- /* debug unknown tags*/
- error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1]));
-
- break;
- }
- if (!$ignore) {
- $eval .= $token[1]." ";
- $ts[] = array("token" => $token[0], "value" => $token[1]);
- }
- } else {
- $ts[] = array("token" => $token, "value" => '');
-
- $last = count($ts) - 1;
-
- switch ($token) {
- case '(':
- /* walk backwards through the tokens */
-
- if ($last >= 4 &&
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
- $ts[$last - 3]['token'] == ')' ) {
- /* func()->method()
- *
- * we can't know what func() is return, so we can't
- * say if the method() exists or not
- *
- */
- } else if ($last >= 3 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
- $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
- $ts[$last - 3]['token'] == T_VARIABLE ) {
-
- /* $object->method( */
-
- /* catch (Exception $e) does not set $e in $GLOBALS[] */
- $in_catch = 0;
-
- foreach ($ts as $v) {
- if ($v['token'] == T_CATCH) {
- $in_catch = 1;
- }
- }
-
- if (!$in_catch) {
- /* $object has to exist and has to be a object */
- $objname = $ts[$last - 3]['value'];
-
- if (!isset($GLOBALS[ltrim($objname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
- }
- $object = $GLOBALS[ltrim($objname, '$')];
-
- if (!is_object($object)) {
- throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
- }
-
- $method = $ts[$last - 1]['value'];
-
- /* obj */
-
- if (!method_exists($object, $method)) {
- throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
- $objname, get_class($object), $method));
- }
- }
- } else if ($last >= 3 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_VARIABLE &&
- $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
- $ts[$last - 3]['token'] == T_VARIABLE ) {
-
- /* $object->$method( */
-
- /* $object has to exist and has to be a object */
- $objname = $ts[$last - 3]['value'];
-
- if (!isset($GLOBALS[ltrim($objname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
- }
- $object = $GLOBALS[ltrim($objname, '$')];
-
- if (!is_object($object)) {
- throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
- }
-
- $methodname = $ts[$last - 1]['value'];
-
- if (!isset($GLOBALS[ltrim($methodname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
- }
- $method = $GLOBALS[ltrim($methodname, '$')];
-
- /* obj */
-
- if (!method_exists($object, $method)) {
- throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
- $objname, get_class($object), $method));
- }
-
- } else if ($last >= 6 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
- $ts[$last - 3]['token'] == ']' &&
- /* might be anything as index */
- $ts[$last - 5]['token'] == '[' &&
- $ts[$last - 6]['token'] == T_VARIABLE ) {
-
- /* $object[...]->method( */
-
- /* $object has to exist and has to be a object */
- $objname = $ts[$last - 6]['value'];
-
- if (!isset($GLOBALS[ltrim($objname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
- }
- $array = $GLOBALS[ltrim($objname, '$')];
-
- if (!is_array($array)) {
- throw new Exception(sprintf('Variable \'%s\' is not a array', $objname));
- }
-
- $andx = $ts[$last - 4]['value'];
-
- if (!isset($array[$andx])) {
- throw new Exception(sprintf('%s[\'%s\'] is not set', $objname, $andx));
- }
-
- $object = $array[$andx];
-
- if (!is_object($object)) {
- throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
- }
-
- $method = $ts[$last - 1]['value'];
-
- /* obj */
-
- if (!method_exists($object, $method)) {
- throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
- $objname, get_class($object), $method));
- }
-
- } else if ($last >= 3 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
- $ts[$last - 3]['token'] == T_STRING ) {
-
- /* Class::method() */
-
- /* $object has to exist and has to be a object */
- $classname = $ts[$last - 3]['value'];
-
- if (!class_exists($classname)) {
- throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
- }
-
- $method = $ts[$last - 1]['value'];
-
- if (!in_array($method, get_class_methods($classname))) {
- throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
- $classname, $method));
- }
- } else if ($last >= 3 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_VARIABLE &&
- $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
- $ts[$last - 3]['token'] == T_STRING ) {
-
- /* $var::method() */
-
- /* $object has to exist and has to be a object */
- $classname = $ts[$last - 3]['value'];
-
- if (!class_exists($classname)) {
- throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
- }
-
- $methodname = $ts[$last - 1]['value'];
-
- if (!isset($GLOBALS[ltrim($methodname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
- }
- $method = $GLOBALS[ltrim($methodname, '$')];
-
- if (!in_array($method, get_class_methods($classname))) {
- throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
- $classname, $method));
- }
-
- } else if ($last >= 2 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_NEW ) {
-
- /* new Class() */
-
- /* don't care about this in a class ... { ... } */
-
- $classname = $ts[$last - 1]['value'];
-
- if (!class_exists($classname)) {
- throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
- }
-
- $r = new ReflectionClass($classname);
-
- if ($r->isAbstract()) {
- throw new Exception(sprintf("Can't instantiate abstract Class '%s'", $classname));
- }
-
- if (!$r->isInstantiable()) {
- throw new Exception(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname));
- }
-
- } else if ($last >= 2 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_FUNCTION ) {
-
- /* make sure we are not a in class definition */
-
- /* function a() */
-
- $func = $ts[$last - 1]['value'];
-
- if (function_exists($func)) {
- throw new Exception(sprintf('Function \'%s\' is already defined', $func));
- }
- } else if ($last >= 4 &&
- $ts[0]['token'] == T_CLASS &&
- $ts[1]['token'] == T_STRING &&
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_FUNCTION ) {
-
- /* make sure we are not a in class definition */
-
- /* class a { .. function a() ... } */
-
- $func = $ts[$last - 1]['value'];
- $classname = $ts[1]['value'];
-
- if (isset($methods[$func])) {
- throw new Exception(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname));
- }
-
- $methods[$func] = 1;
-
- } else if ($last >= 1 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
- $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_STRING ) {
- /* func() */
- $funcname = $ts[$last - 1]['value'];
-
- if (!function_exists($funcname)) {
- throw new Exception(sprintf("Function %s() doesn't exist", $funcname));
- }
- } else if ($last >= 1 &&
- $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_VARIABLE ) {
-
- /* $object has to exist and has to be a object */
- $funcname = $ts[$last - 1]['value'];
-
- if (!isset($GLOBALS[ltrim($funcname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $funcname));
- }
- $func = $GLOBALS[ltrim($funcname, '$')];
-
- if (!function_exists($func)) {
- throw new Exception(sprintf("Function %s() doesn't exist", $func));
- }
-
- }
-
- array_push($braces, $token);
- break;
- case '{':
- $need_return = 0;
-
- if ($last >= 2 &&
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_CLASS ) {
-
- /* class name { */
-
- $classname = $ts[$last - 1]['value'];
-
- if (class_exists($classname, false)) {
- throw new Exception(sprintf("Class '%s' can't be redeclared", $classname));
- }
- } else if ($last >= 4 &&
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_EXTENDS &&
- $ts[$last - 3]['token'] == T_STRING &&
- $ts[$last - 4]['token'] == T_CLASS ) {
-
- /* class classname extends classname { */
-
- $classname = $ts[$last - 3]['value'];
- $extendsname = $ts[$last - 1]['value'];
-
- if (class_exists($classname, false)) {
- throw new Exception(sprintf("Class '%s' can't be redeclared",
- $classname));
- }
- if (!class_exists($extendsname, true)) {
- throw new Exception(sprintf("Can't extend '%s' ... from not existing Class '%s'",
- $classname, $extendsname));
- }
- } else if ($last >= 4 &&
- $ts[$last - 1]['token'] == T_STRING &&
- $ts[$last - 2]['token'] == T_IMPLEMENTS &&
- $ts[$last - 3]['token'] == T_STRING &&
- $ts[$last - 4]['token'] == T_CLASS ) {
-
- /* class name implements interface { */
-
- $classname = $ts[$last - 3]['value'];
- $implements = $ts[$last - 1]['value'];
-
- if (class_exists($classname, false)) {
- throw new Exception(sprintf("Class '%s' can't be redeclared",
- $classname));
- }
- if (!interface_exists($implements, false)) {
- throw new Exception(sprintf("Can't implement not existing Interface '%s' for Class '%s'",
- $implements, $classname));
- }
- }
-
- array_push($braces, $token);
- break;
- case '}':
- $need_return = 0;
- case ')':
- array_pop($braces);
- break;
- case '[':
- if ($ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
- $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
- $ts[$last - 1]['token'] == T_VARIABLE) {
- /* $a[] only works on array and string */
-
- /* $object has to exist and has to be a object */
- $objname = $ts[$last - 1]['value'];
-
- if (!isset($GLOBALS[ltrim($objname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
- }
- $obj = $GLOBALS[ltrim($objname, '$')];
-
- if (is_object($obj)) {
- throw new Exception(sprintf('Objects (%s) don\'t support array access operators', $objname));
- }
- }
- break;
- }
-
- $eval .= $token;
- }
- }
-
- $last = count($ts) - 1;
- if ($last >= 2 &&
- $ts[$last - 0]['token'] == T_STRING &&
- $ts[$last - 1]['token'] == T_DOUBLE_COLON &&
- $ts[$last - 2]['token'] == T_STRING ) {
-
- /* Class::constant */
-
- /* $object has to exist and has to be a object */
- $classname = $ts[$last - 2]['value'];
-
- if (!class_exists($classname)) {
- throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
- }
-
- $constname = $ts[$last - 0]['value'];
-
- $c = new ReflectionClass($classname);
- if (!$c->hasConstant($constname)) {
- throw new Exception(sprintf("Class '%s' doesn't have a constant named '%s'",
- $classname, $constname));
- }
- } else if ($last == 0 &&
- $ts[$last - 0]['token'] == T_VARIABLE ) {
-
- /* $var */
-
- $varname = $ts[$last - 0]['value'];
-
- if (!isset($GLOBALS[ltrim($varname, '$')])) {
- throw new Exception(sprintf('Variable \'%s\' is not set', $varname));
- }
- }
-
-
- $need_more = (count($braces) > 0) || $open_comment;
-
- if ($need_more || ';' === $token) {
- $need_semicolon = 0;
- }
-
- if ($need_return) {
- $eval = "return ".$eval;
- }
-
- /* add a traling ; if necessary */
- if ($need_semicolon)
- {
- $this->has_semicolon = preg_match('/;\s*$/', $eval);
- $eval .= ';';
- }
-
- if (!$need_more) {
- $this->code = $eval;
- }
-
- return $need_more;
- }
-
- /**
- * show the prompt and fetch a single line
- *
- * uses readline() if avaialbe
- *
- * @return string a input-line
- */
- public function readline() {
- if (empty($this->code)) print PHP_EOL;
-
- $prompt = (empty($this->code)) ? '>> ' : '.. ';
-
- if (count($this->code_buffer) > 0) {
- print $prompt;
-
- $line = array_shift($this->code_buffer);
-
- print $line.PHP_EOL;
-
- return $line.PHP_EOL;
- }
-
- if ($this->have_readline) {
- $l = readline($prompt);
-
- readline_add_history($l);
- } else {
- print $prompt;
-
- if (is_null($this->stdin)) {
- if (false === ($this->stdin = fopen("php://stdin", "r"))) {
- return false;
- }
- }
- $l = fgets($this->stdin);
- }
- return $l;
- }
-
- /**
- * get the inline help
- *
- * @return string the inline help as string
- */
- public function cmdHelp($l) {
- $o = 'Inline Help:'.PHP_EOL;
-
- $cmds = PHP_Shell_Commands::getInstance()->getCommands();
-
- $help = array();
- foreach ($cmds as $cmd) {
- $help[] = sprintf(' >> %s'.PHP_EOL.' %s'.PHP_EOL,
- $cmd['command'],
- $cmd['description']
- );
- }
-
- return var_export(implode("\n", $help), 1);
- }
-
- /**
- * get the license string
- *
- * @return string the inline help as string
- */
- public function cmdLicense($l) {
- $o = <<
-
-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.
-EOF;
-
- return var_export($o, 1);
- }
-
- /**
- * handle the 'quit' command
- *
- * @return bool false to leave the input() call
- * @see input
- */
- protected function cmdQuit($l) {
- return false;
- }
-
- /**
- * handle the input line
- *
- * read the input and handle the commands of the shell
- *
- * @return bool false on 'quit' or EOF, true otherwise
- */
- public function input() {
- $l = $this->readline();
-
- /* got EOF ? */
- if (false === $l) return false;
-
- $l = trim($l);
-
- if (empty($this->code)) {
- $this->verbose = 0;
-
- $cmds = PHP_Shell_Commands::getInstance()->getCommands();
-
- foreach ($cmds as $cmd) {
- if (preg_match($cmd['regex'], $l)) {
- $obj = $cmd['obj'];
- $func = $cmd['method'];
-
- if (false === ($l = $obj->$func($l))) {
- ## quit
- return false;
- }
-
- if (is_array($l)) {
- $this->code_buffer = $l;
- $l = '';
- }
- break;
- }
- }
- }
-
- $this->appendCode($l);
-
- return true;
- }
-
- /**
- * get the code-buffer
- *
- * @return string the code-buffer
- */
- public function getCode() {
- return $this->code;
- return $code;
- }
-
- /**
- * reset the code-buffer
- */
- public function resetCode() {
- $this->has_semicolon=false;
- $this->code = '';
- }
-
- /**
- * append code to the code-buffer
- *
- * @param string $code input buffer
- */
- public function appendCode($code) {
- if (strlen($code)) $code .= PHP_EOL;
-
- $this->code .= $code;
- }
-
- /**
- * check if readline support is enabled
- *
- * @return bool true if enabled, false otherwise
- */
- public function hasReadline() {
- return $this->have_readline;
- }
-
- /**
- * get version of the class
- *
- * @return string version-string
- */
- public function getVersion() {
- return $this->version;
- }
-}
-
-/**
-* a readline completion callback
-*
-* @param string $str linebuffer
-* @param integer $pos position in linebuffer
-* @return array list of possible matches
-*/
-function __shell_readline_complete($str, $pos) {
- $in = readline_info('line_buffer');
-
- /**
- * parse the line-buffer backwards to see if we have a
- * - constant
- * - function
- * - variable
- */
-
- $m = array();
-
- if (preg_match('#\$([A-Za-z0-9_]+)->#', $in, $a)) {
- /* check for $o->... */
- $name = $a[1];
-
- if (isset($GLOBALS[$name]) && is_object($GLOBALS[$name])) {
- $c = get_class_methods($GLOBALS[$name]);
-
- foreach ($c as $v) {
- $m[] = $v.'(';
- }
- $c = get_class_vars(get_class($GLOBALS[$name]));
-
- foreach ($c as $k => $v) {
- $m[] = $k;
- }
-
- return $m;
- }
- } else if (preg_match('#\$([A-Za-z0-9_]+)\[([^\]]+)\]->#', $in, $a)) {
- /* check for $o[...]->... */
- $name = $a[1];
-
- if (isset($GLOBALS[$name]) &&
- is_array($GLOBALS[$name]) &&
- isset($GLOBALS[$name][$a[2]])) {
-
- $c = get_class_methods($GLOBALS[$name][$a[2]]);
-
- foreach ($c as $v) {
- $m[] = $v.'(';
- }
- $c = get_class_vars(get_class($GLOBALS[$name][$a[2]]));
-
- foreach ($c as $k => $v) {
- $m[] = $k;
- }
- return $m;
- }
-
- } else if (preg_match('#([A-Za-z0-9_]+)::#', $in, $a)) {
- /* check for Class:: */
- $name = $a[1];
-
- if (class_exists($name, false)) {
- $c = get_class_methods($name);
-
- foreach ($c as $v) {
- $m[] = sprintf('%s::%s(', $name, $v);
- }
-
- $cl = new ReflectionClass($name);
- $c = $cl->getConstants();
-
- foreach ($c as $k => $v) {
- $m[] = sprintf('%s::%s', $name, $k);
- }
-
- return $m;
- }
- } else if (preg_match('#\$([a-zA-Z]?[a-zA-Z0-9_]*)$#', $in)) {
- $m = array_keys($GLOBALS);
-
- return $m;
- } else if (preg_match('#new #', $in)) {
- $c = get_declared_classes();
-
- foreach ($c as $v) {
- $m[] = $v.'(';
- }
-
- return $m;
- } else if (preg_match('#^:set #', $in)) {
- foreach (PHP_Shell_Options::getInstance()->getOptions() as $v) {
- $m[] = $v;
- }
-
- return $m;
- }
-
- $f = get_defined_functions();
-
- foreach ($f['internal'] as $v) {
- $m[] = $v.'(';
- }
-
- foreach ($f['user'] as $v) {
- $m[] = $v.'(';
- }
-
- $c = get_declared_classes();
-
- foreach ($c as $v) {
- $m[] = $v.'::';
- }
-
- $c = get_defined_constants();
-
- foreach ($c as $k => $v) {
- $m[] = $k;
- }
-
- /* taken from http://de3.php.net/manual/en/reserved.php */
- $m[] = 'abstract';
- $m[] = 'and';
- $m[] = 'array(';
- $m[] = 'as';
- $m[] = 'break';
- $m[] = 'case';
- $m[] = 'catch';
- $m[] = 'class';
- $m[] = 'const';
- $m[] = 'continue';
- # $m[] = 'declare';
- $m[] = 'default';
- $m[] = 'die(';
- $m[] = 'do';
- $m[] = 'echo(';
- $m[] = 'else';
- $m[] = 'elseif';
- $m[] = 'empty(';
- # $m[] = 'enddeclare';
- $m[] = 'eval(';
- $m[] = 'exception';
- $m[] = 'extends';
- $m[] = 'exit(';
- $m[] = 'extends';
- $m[] = 'final';
- $m[] = 'for (';
- $m[] = 'foreach (';
- $m[] = 'function';
- $m[] = 'global';
- $m[] = 'if';
- $m[] = 'implements';
- $m[] = 'include "';
- $m[] = 'include_once "';
- $m[] = 'interface';
- $m[] = 'isset(';
- $m[] = 'list(';
- $m[] = 'new';
- $m[] = 'or';
- $m[] = 'print(';
- $m[] = 'private';
- $m[] = 'protected';
- $m[] = 'public';
- $m[] = 'require "';
- $m[] = 'require_once "';
- $m[] = 'return';
- $m[] = 'static';
- $m[] = 'switch (';
- $m[] = 'throw';
- $m[] = 'try';
- $m[] = 'unset(';
- # $m[] = 'use';
- $m[] = 'var';
- $m[] = 'while';
- $m[] = 'xor';
- $m[] = '__FILE__';
- $m[] = '__FUNCTION__';
- $m[] = '__CLASS__';
- $m[] = '__LINE__';
- $m[] = '__METHOD__';
-
- # printf("%s ... %s\n", $str, $pos);
- return $m;
-}
-
-
+
+
+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.
+*/
+
+/**
+* A interactive PHP Shell
+*
+* The more I work with other languages like python and ruby I like their way how they
+* work on problems. While PHP is very forgiving on errors, it is weak on the debugging
+* side. It was missing a simple to use interactive shell for years. Python and Ruby have
+* their ipython and iruby shell which give you a direct way to interact with the objects.
+* No need to write a script and execute it afterwards.
+*
+* Starting the Shell:
+*
+* The package contains a shell wrapper for windows and unix:
+*
+* sh> php-shell.sh
+* win> php-shell
+*
+*
+* Both are calling the wrapper script php -q php-shell-cmd.php
+*
+* Inline Help
+*
+*
+* PHP-Shell - Version 0.2.0, with readline() support
+* (c) 2006, Jan Kneschke
+*
+* >> use '?' to open the inline help
+*
+* >> ?
+* "inline help for the PHP-shell
+*
+* >> ?
+* print this help
+* >> ?
+* get the doccomment for a class, method, property or function
+* >> p
+* execute a verbose print (if implemented)
+* >> quit
+* leave shell
+* "
+* >> ? PHP_Shell
+*
+* Alternatives
+*
+* - http://david.acz.org/phpa/
+* - http://www.hping.org/phpinteractive/
+* - the embedded interactive php-shell: $ php -a
+*
+* @package PHP
+*/
+
+/**
+* PHP_Shell
+*
+* a interactive PHP Shell with tab-completion and history
+* it can catch FATAL errors before executing the code
+*
+* Extensions are provided through three side-classes:
+*
+* - PHP_Shell_Commands
+* - PHP_Shell_Options
+* - PHP_Shell_Extensions
+*
+* @package PHP
+*/
+
+require_once(dirname(__FILE__)."/Shell/Commands.php");
+require_once(dirname(__FILE__)."/Shell/Options.php"); /* for the tab-complete */
+
+class PHP_Shell {
+ /**
+ * current code-buffer
+ * @var string
+ */
+ protected $code;
+
+ /**
+ * set if readline support is enabled
+ * @var bool
+ */
+ protected $have_readline;
+
+ /**
+ * current version of the class
+ * @var string
+ */
+ protected $version = '0.3.1';
+
+ /**
+ *
+ */
+ protected $stdin;
+
+ protected $code_buffer;
+
+ public $has_semicolon=false;
+
+ /**
+ * init the shell and change if readline support is available
+ */
+ public function __construct() {
+ $this->code = '';
+
+ $this->stdin = null;
+
+ $this->have_readline = function_exists('readline');
+
+ if ($this->have_readline) {
+ readline_completion_function('__shell_readline_complete');
+ }
+
+ $this->use_readline = true;
+
+ $cmd = PHP_Shell_Commands::getInstance();
+
+ $cmd->registerCommand('#^quit$#', $this, 'cmdQuit', 'quit', 'leaves the shell');
+ $cmd->registerCommand('#^\?$#', $this, 'cmdHelp', '?', 'show this help');
+ $cmd->registerCommand('#^\?\s+license$#', $this, 'cmdLicense', '? license', 'show license of the shell');
+ }
+
+
+ /**
+ * parse the PHP code
+ *
+ * we parse before we eval() the code to
+ * - fetch fatal errors before they come up
+ * - know about where we have to wait for closing braces
+ *
+ * @return int 0 if a executable statement is in the code-buffer, non-zero otherwise
+ */
+ public function parse() {
+ ## remove empty lines
+ if (trim($this->code) == '') return 1;
+
+ $t = token_get_all('code.' ?>');
+
+ $need_semicolon = 1; /* do we need a semicolon to complete the statement ? */
+ $need_return = 1; /* can we prepend a return to the eval-string ? */
+ $open_comment = 0; /* a open multi-line comment */
+ $eval = ''; /* code to be eval()'ed later */
+ $braces = array(); /* to track if we need more closing braces */
+
+ $methods = array(); /* to track duplicate methods in a class declaration */
+ $ts = array(); /* tokens without whitespaces */
+
+ foreach ($t as $ndx => $token) {
+ if (is_array($token)) {
+ $ignore = 0;
+
+ switch($token[0]) {
+ case T_WHITESPACE:
+ case T_OPEN_TAG:
+ case T_CLOSE_TAG:
+ $ignore = 1;
+ break;
+ case T_FOREACH:
+ case T_DO:
+ case T_WHILE:
+ case T_FOR:
+
+ case T_IF:
+ case T_RETURN:
+
+ case T_CLASS:
+ case T_FUNCTION:
+ case T_INTERFACE:
+
+ case T_PRINT:
+ case T_ECHO:
+
+ case T_COMMENT:
+ case T_UNSET:
+
+ case T_INCLUDE:
+ case T_REQUIRE:
+ case T_INCLUDE_ONCE:
+ case T_REQUIRE_ONCE:
+ case T_TRY:
+ case T_SWITCH:
+ case T_DEFAULT:
+ case T_CASE:
+ case T_BREAK:
+ case T_DOC_COMMENT:
+ $need_return = 0;
+ break;
+ case T_EMPTY:
+ case T_ISSET:
+ case T_EVAL:
+ case T_EXIT:
+
+ case T_VARIABLE:
+ case T_STRING:
+ case T_NEW:
+ case T_EXTENDS:
+ case T_IMPLEMENTS:
+ case T_OBJECT_OPERATOR:
+ case T_DOUBLE_COLON:
+ case T_INSTANCEOF:
+
+ case T_CATCH:
+ case T_THROW:
+
+ case T_ELSE:
+ case T_AS:
+ case T_LNUMBER:
+ case T_DNUMBER:
+ case T_CONSTANT_ENCAPSED_STRING:
+ case T_ENCAPSED_AND_WHITESPACE:
+ case T_CHARACTER:
+ case T_ARRAY:
+ case T_DOUBLE_ARROW:
+
+ case T_CONST:
+ case T_PUBLIC:
+ case T_PROTECTED:
+ case T_PRIVATE:
+ case T_ABSTRACT:
+ case T_STATIC:
+ case T_VAR:
+
+ case T_INC:
+ case T_DEC:
+ case T_SL:
+ case T_SL_EQUAL:
+ case T_SR:
+ case T_SR_EQUAL:
+
+ case T_IS_EQUAL:
+ case T_IS_IDENTICAL:
+ case T_IS_GREATER_OR_EQUAL:
+ case T_IS_SMALLER_OR_EQUAL:
+
+ case T_BOOLEAN_OR:
+ case T_LOGICAL_OR:
+ case T_BOOLEAN_AND:
+ case T_LOGICAL_AND:
+ case T_LOGICAL_XOR:
+ case T_MINUS_EQUAL:
+ case T_PLUS_EQUAL:
+ case T_MUL_EQUAL:
+ case T_DIV_EQUAL:
+ case T_MOD_EQUAL:
+ case T_XOR_EQUAL:
+ case T_AND_EQUAL:
+ case T_OR_EQUAL:
+
+ case T_FUNC_C:
+ case T_CLASS_C:
+ case T_LINE:
+ case T_FILE:
+
+ case T_BOOL_CAST:
+ case T_INT_CAST:
+ case T_STRING_CAST:
+
+ /* just go on */
+ break;
+ default:
+ /* debug unknown tags*/
+ error_log(sprintf("unknown tag: %d (%s): %s".PHP_EOL, $token[0], token_name($token[0]), $token[1]));
+
+ break;
+ }
+ if (!$ignore) {
+ $eval .= $token[1]." ";
+ $ts[] = array("token" => $token[0], "value" => $token[1]);
+ }
+ } else {
+ $ts[] = array("token" => $token, "value" => '');
+
+ $last = count($ts) - 1;
+
+ switch ($token) {
+ case '(':
+ /* walk backwards through the tokens */
+
+ if ($last >= 4 &&
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == ')' ) {
+ /* func()->method()
+ *
+ * we can't know what func() is return, so we can't
+ * say if the method() exists or not
+ *
+ */
+ } else if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
+ $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+ /* $object->method( */
+
+ /* catch (Exception $e) does not set $e in $GLOBALS[] */
+ $in_catch = 0;
+
+ foreach ($ts as $v) {
+ if ($v['token'] == T_CATCH) {
+ $in_catch = 1;
+ }
+ }
+
+ if (!$in_catch) {
+ /* $object has to exist and has to be a object */
+ $objname = $ts[$last - 3]['value'];
+
+ if (!isset($GLOBALS[ltrim($objname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
+ }
+ $object = $GLOBALS[ltrim($objname, '$')];
+
+ if (!is_object($object)) {
+ throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
+ }
+
+ $method = $ts[$last - 1]['value'];
+
+ /* obj */
+
+ if (!method_exists($object, $method)) {
+ throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
+ $objname, get_class($object), $method));
+ }
+ }
+ } else if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_VARIABLE &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == T_VARIABLE ) {
+
+ /* $object->$method( */
+
+ /* $object has to exist and has to be a object */
+ $objname = $ts[$last - 3]['value'];
+
+ if (!isset($GLOBALS[ltrim($objname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
+ }
+ $object = $GLOBALS[ltrim($objname, '$')];
+
+ if (!is_object($object)) {
+ throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
+ }
+
+ $methodname = $ts[$last - 1]['value'];
+
+ if (!isset($GLOBALS[ltrim($methodname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
+ }
+ $method = $GLOBALS[ltrim($methodname, '$')];
+
+ /* obj */
+
+ if (!method_exists($object, $method)) {
+ throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
+ $objname, get_class($object), $method));
+ }
+
+ } else if ($last >= 6 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_OBJECT_OPERATOR &&
+ $ts[$last - 3]['token'] == ']' &&
+ /* might be anything as index */
+ $ts[$last - 5]['token'] == '[' &&
+ $ts[$last - 6]['token'] == T_VARIABLE ) {
+
+ /* $object[...]->method( */
+
+ /* $object has to exist and has to be a object */
+ $objname = $ts[$last - 6]['value'];
+
+ if (!isset($GLOBALS[ltrim($objname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
+ }
+ $array = $GLOBALS[ltrim($objname, '$')];
+
+ if (!is_array($array)) {
+ throw new Exception(sprintf('Variable \'%s\' is not a array', $objname));
+ }
+
+ $andx = $ts[$last - 4]['value'];
+
+ if (!isset($array[$andx])) {
+ throw new Exception(sprintf('%s[\'%s\'] is not set', $objname, $andx));
+ }
+
+ $object = $array[$andx];
+
+ if (!is_object($object)) {
+ throw new Exception(sprintf('Variable \'%s\' is not a class', $objname));
+ }
+
+ $method = $ts[$last - 1]['value'];
+
+ /* obj */
+
+ if (!method_exists($object, $method)) {
+ throw new Exception(sprintf("Variable %s (Class '%s') doesn't have a method named '%s'",
+ $objname, get_class($object), $method));
+ }
+
+ } else if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
+ $ts[$last - 3]['token'] == T_STRING ) {
+
+ /* Class::method() */
+
+ /* $object has to exist and has to be a object */
+ $classname = $ts[$last - 3]['value'];
+
+ if (!class_exists($classname)) {
+ throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
+ }
+
+ $method = $ts[$last - 1]['value'];
+
+ if (!in_array($method, get_class_methods($classname))) {
+ throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
+ $classname, $method));
+ }
+ } else if ($last >= 3 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_VARIABLE &&
+ $ts[$last - 2]['token'] == T_DOUBLE_COLON &&
+ $ts[$last - 3]['token'] == T_STRING ) {
+
+ /* $var::method() */
+
+ /* $object has to exist and has to be a object */
+ $classname = $ts[$last - 3]['value'];
+
+ if (!class_exists($classname)) {
+ throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
+ }
+
+ $methodname = $ts[$last - 1]['value'];
+
+ if (!isset($GLOBALS[ltrim($methodname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $methodname));
+ }
+ $method = $GLOBALS[ltrim($methodname, '$')];
+
+ if (!in_array($method, get_class_methods($classname))) {
+ throw new Exception(sprintf("Class '%s' doesn't have a method named '%s'",
+ $classname, $method));
+ }
+
+ } else if ($last >= 2 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_NEW ) {
+
+ /* new Class() */
+
+ /* don't care about this in a class ... { ... } */
+
+ $classname = $ts[$last - 1]['value'];
+
+ if (!class_exists($classname)) {
+ throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
+ }
+
+ $r = new ReflectionClass($classname);
+
+ if ($r->isAbstract()) {
+ throw new Exception(sprintf("Can't instantiate abstract Class '%s'", $classname));
+ }
+
+ if (!$r->isInstantiable()) {
+ throw new Exception(sprintf('Class \'%s\' can\'t be instantiated. Is the class abstract ?', $classname));
+ }
+
+ } else if ($last >= 2 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_FUNCTION ) {
+
+ /* make sure we are not a in class definition */
+
+ /* function a() */
+
+ $func = $ts[$last - 1]['value'];
+
+ if (function_exists($func)) {
+ throw new Exception(sprintf('Function \'%s\' is already defined', $func));
+ }
+ } else if ($last >= 4 &&
+ $ts[0]['token'] == T_CLASS &&
+ $ts[1]['token'] == T_STRING &&
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_FUNCTION ) {
+
+ /* make sure we are not a in class definition */
+
+ /* class a { .. function a() ... } */
+
+ $func = $ts[$last - 1]['value'];
+ $classname = $ts[1]['value'];
+
+ if (isset($methods[$func])) {
+ throw new Exception(sprintf("Can't redeclare method '%s' in Class '%s'", $func, $classname));
+ }
+
+ $methods[$func] = 1;
+
+ } else if ($last >= 1 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
+ $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_STRING ) {
+ /* func() */
+ $funcname = $ts[$last - 1]['value'];
+
+ if (!function_exists($funcname)) {
+ throw new Exception(sprintf("Function %s() doesn't exist", $funcname));
+ }
+ } else if ($last >= 1 &&
+ $ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_VARIABLE ) {
+
+ /* $object has to exist and has to be a object */
+ $funcname = $ts[$last - 1]['value'];
+
+ if (!isset($GLOBALS[ltrim($funcname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $funcname));
+ }
+ $func = $GLOBALS[ltrim($funcname, '$')];
+
+ if (!function_exists($func)) {
+ throw new Exception(sprintf("Function %s() doesn't exist", $func));
+ }
+
+ }
+
+ array_push($braces, $token);
+ break;
+ case '{':
+ $need_return = 0;
+
+ if ($last >= 2 &&
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_CLASS ) {
+
+ /* class name { */
+
+ $classname = $ts[$last - 1]['value'];
+
+ if (class_exists($classname, false)) {
+ throw new Exception(sprintf("Class '%s' can't be redeclared", $classname));
+ }
+ } else if ($last >= 4 &&
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_EXTENDS &&
+ $ts[$last - 3]['token'] == T_STRING &&
+ $ts[$last - 4]['token'] == T_CLASS ) {
+
+ /* class classname extends classname { */
+
+ $classname = $ts[$last - 3]['value'];
+ $extendsname = $ts[$last - 1]['value'];
+
+ if (class_exists($classname, false)) {
+ throw new Exception(sprintf("Class '%s' can't be redeclared",
+ $classname));
+ }
+ if (!class_exists($extendsname, true)) {
+ throw new Exception(sprintf("Can't extend '%s' ... from not existing Class '%s'",
+ $classname, $extendsname));
+ }
+ } else if ($last >= 4 &&
+ $ts[$last - 1]['token'] == T_STRING &&
+ $ts[$last - 2]['token'] == T_IMPLEMENTS &&
+ $ts[$last - 3]['token'] == T_STRING &&
+ $ts[$last - 4]['token'] == T_CLASS ) {
+
+ /* class name implements interface { */
+
+ $classname = $ts[$last - 3]['value'];
+ $implements = $ts[$last - 1]['value'];
+
+ if (class_exists($classname, false)) {
+ throw new Exception(sprintf("Class '%s' can't be redeclared",
+ $classname));
+ }
+ if (!interface_exists($implements, false)) {
+ throw new Exception(sprintf("Can't implement not existing Interface '%s' for Class '%s'",
+ $implements, $classname));
+ }
+ }
+
+ array_push($braces, $token);
+ break;
+ case '}':
+ $need_return = 0;
+ case ')':
+ array_pop($braces);
+ break;
+ case '[':
+ if ($ts[0]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[0]['token'] != T_ABSTRACT && /* if we are not in a class definition */
+ $ts[1]['token'] != T_CLASS && /* if we are not in a class definition */
+ $ts[$last - 1]['token'] == T_VARIABLE) {
+ /* $a[] only works on array and string */
+
+ /* $object has to exist and has to be a object */
+ $objname = $ts[$last - 1]['value'];
+
+ if (!isset($GLOBALS[ltrim($objname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $objname));
+ }
+ $obj = $GLOBALS[ltrim($objname, '$')];
+
+ if (is_object($obj)) {
+ throw new Exception(sprintf('Objects (%s) don\'t support array access operators', $objname));
+ }
+ }
+ break;
+ }
+
+ $eval .= $token;
+ }
+ }
+
+ $last = count($ts) - 1;
+ if ($last >= 2 &&
+ $ts[$last - 0]['token'] == T_STRING &&
+ $ts[$last - 1]['token'] == T_DOUBLE_COLON &&
+ $ts[$last - 2]['token'] == T_STRING ) {
+
+ /* Class::constant */
+
+ /* $object has to exist and has to be a object */
+ $classname = $ts[$last - 2]['value'];
+
+ if (!class_exists($classname)) {
+ throw new Exception(sprintf('Class \'%s\' doesn\'t exist', $classname));
+ }
+
+ $constname = $ts[$last - 0]['value'];
+
+ $c = new ReflectionClass($classname);
+ if (!$c->hasConstant($constname)) {
+ throw new Exception(sprintf("Class '%s' doesn't have a constant named '%s'",
+ $classname, $constname));
+ }
+ } else if ($last == 0 &&
+ $ts[$last - 0]['token'] == T_VARIABLE ) {
+
+ /* $var */
+
+ $varname = $ts[$last - 0]['value'];
+
+ if (!isset($GLOBALS[ltrim($varname, '$')])) {
+ throw new Exception(sprintf('Variable \'%s\' is not set', $varname));
+ }
+ }
+
+
+ $need_more = (count($braces) > 0) || $open_comment;
+
+ if ($need_more || ';' === $token) {
+ $need_semicolon = 0;
+ }
+
+ if ($need_return) {
+ $eval = "return ".$eval;
+ }
+
+ /* add a traling ; if necessary */
+ if ($need_semicolon)
+ {
+ $this->has_semicolon = preg_match('/;\s*$/', $eval);
+ $eval .= ';';
+ }
+
+ if (!$need_more) {
+ $this->code = $eval;
+ }
+
+ return $need_more;
+ }
+
+ /**
+ * show the prompt and fetch a single line
+ *
+ * uses readline() if avaialbe
+ *
+ * @return string a input-line
+ */
+ public function readline() {
+ if (empty($this->code)) print PHP_EOL;
+
+ $prompt = (empty($this->code)) ? '>> ' : '.. ';
+
+ if (count($this->code_buffer) > 0) {
+ print $prompt;
+
+ $line = array_shift($this->code_buffer);
+
+ print $line.PHP_EOL;
+
+ return $line.PHP_EOL;
+ }
+
+ if ($this->have_readline) {
+ $l = readline($prompt);
+
+ readline_add_history($l);
+ } else {
+ print $prompt;
+
+ if (is_null($this->stdin)) {
+ if (false === ($this->stdin = fopen("php://stdin", "r"))) {
+ return false;
+ }
+ }
+ $l = fgets($this->stdin);
+ }
+ return $l;
+ }
+
+ /**
+ * get the inline help
+ *
+ * @return string the inline help as string
+ */
+ public function cmdHelp($l) {
+ $o = 'Inline Help:'.PHP_EOL;
+
+ $cmds = PHP_Shell_Commands::getInstance()->getCommands();
+
+ $help = array();
+ foreach ($cmds as $cmd) {
+ $help[] = sprintf(' >> %s'.PHP_EOL.' %s'.PHP_EOL,
+ $cmd['command'],
+ $cmd['description']
+ );
+ }
+
+ return var_export(implode("\n", $help), 1);
+ }
+
+ /**
+ * get the license string
+ *
+ * @return string the inline help as string
+ */
+ public function cmdLicense($l) {
+ $o = <<
+
+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.
+EOF;
+
+ return var_export($o, 1);
+ }
+
+ /**
+ * handle the 'quit' command
+ *
+ * @return bool false to leave the input() call
+ * @see input
+ */
+ protected function cmdQuit($l) {
+ return false;
+ }
+
+ /**
+ * handle the input line
+ *
+ * read the input and handle the commands of the shell
+ *
+ * @return bool false on 'quit' or EOF, true otherwise
+ */
+ public function input() {
+ $l = $this->readline();
+
+ /* got EOF ? */
+ if (false === $l) return false;
+
+ $l = trim($l);
+
+ if (empty($this->code)) {
+ $this->verbose = 0;
+
+ $cmds = PHP_Shell_Commands::getInstance()->getCommands();
+
+ foreach ($cmds as $cmd) {
+ if (preg_match($cmd['regex'], $l)) {
+ $obj = $cmd['obj'];
+ $func = $cmd['method'];
+
+ if (false === ($l = $obj->$func($l))) {
+ ## quit
+ return false;
+ }
+
+ if (is_array($l)) {
+ $this->code_buffer = $l;
+ $l = '';
+ }
+ break;
+ }
+ }
+ }
+
+ $this->appendCode($l);
+
+ return true;
+ }
+
+ /**
+ * get the code-buffer
+ *
+ * @return string the code-buffer
+ */
+ public function getCode() {
+ return $this->code;
+ return $code;
+ }
+
+ /**
+ * reset the code-buffer
+ */
+ public function resetCode() {
+ $this->has_semicolon=false;
+ $this->code = '';
+ }
+
+ /**
+ * append code to the code-buffer
+ *
+ * @param string $code input buffer
+ */
+ public function appendCode($code) {
+ if (strlen($code)) $code .= PHP_EOL;
+
+ $this->code .= $code;
+ }
+
+ /**
+ * check if readline support is enabled
+ *
+ * @return bool true if enabled, false otherwise
+ */
+ public function hasReadline() {
+ return $this->have_readline;
+ }
+
+ /**
+ * get version of the class
+ *
+ * @return string version-string
+ */
+ public function getVersion() {
+ return $this->version;
+ }
+}
+
+/**
+* a readline completion callback
+*
+* @param string $str linebuffer
+* @param integer $pos position in linebuffer
+* @return array list of possible matches
+*/
+function __shell_readline_complete($str, $pos) {
+ $in = readline_info('line_buffer');
+
+ /**
+ * parse the line-buffer backwards to see if we have a
+ * - constant
+ * - function
+ * - variable
+ */
+
+ $m = array();
+
+ if (preg_match('#\$([A-Za-z0-9_]+)->#', $in, $a)) {
+ /* check for $o->... */
+ $name = $a[1];
+
+ if (isset($GLOBALS[$name]) && is_object($GLOBALS[$name])) {
+ $c = get_class_methods($GLOBALS[$name]);
+
+ foreach ($c as $v) {
+ $m[] = $v.'(';
+ }
+ $c = get_class_vars(get_class($GLOBALS[$name]));
+
+ foreach ($c as $k => $v) {
+ $m[] = $k;
+ }
+
+ return $m;
+ }
+ } else if (preg_match('#\$([A-Za-z0-9_]+)\[([^\]]+)\]->#', $in, $a)) {
+ /* check for $o[...]->... */
+ $name = $a[1];
+
+ if (isset($GLOBALS[$name]) &&
+ is_array($GLOBALS[$name]) &&
+ isset($GLOBALS[$name][$a[2]])) {
+
+ $c = get_class_methods($GLOBALS[$name][$a[2]]);
+
+ foreach ($c as $v) {
+ $m[] = $v.'(';
+ }
+ $c = get_class_vars(get_class($GLOBALS[$name][$a[2]]));
+
+ foreach ($c as $k => $v) {
+ $m[] = $k;
+ }
+ return $m;
+ }
+
+ } else if (preg_match('#([A-Za-z0-9_]+)::#', $in, $a)) {
+ /* check for Class:: */
+ $name = $a[1];
+
+ if (class_exists($name, false)) {
+ $c = get_class_methods($name);
+
+ foreach ($c as $v) {
+ $m[] = sprintf('%s::%s(', $name, $v);
+ }
+
+ $cl = new ReflectionClass($name);
+ $c = $cl->getConstants();
+
+ foreach ($c as $k => $v) {
+ $m[] = sprintf('%s::%s', $name, $k);
+ }
+
+ return $m;
+ }
+ } else if (preg_match('#\$([a-zA-Z]?[a-zA-Z0-9_]*)$#', $in)) {
+ $m = array_keys($GLOBALS);
+
+ return $m;
+ } else if (preg_match('#new #', $in)) {
+ $c = get_declared_classes();
+
+ foreach ($c as $v) {
+ $m[] = $v.'(';
+ }
+
+ return $m;
+ } else if (preg_match('#^:set #', $in)) {
+ foreach (PHP_Shell_Options::getInstance()->getOptions() as $v) {
+ $m[] = $v;
+ }
+
+ return $m;
+ }
+
+ $f = get_defined_functions();
+
+ foreach ($f['internal'] as $v) {
+ $m[] = $v.'(';
+ }
+
+ foreach ($f['user'] as $v) {
+ $m[] = $v.'(';
+ }
+
+ $c = get_declared_classes();
+
+ foreach ($c as $v) {
+ $m[] = $v.'::';
+ }
+
+ $c = get_defined_constants();
+
+ foreach ($c as $k => $v) {
+ $m[] = $k;
+ }
+
+ /* taken from http://de3.php.net/manual/en/reserved.php */
+ $m[] = 'abstract';
+ $m[] = 'and';
+ $m[] = 'array(';
+ $m[] = 'as';
+ $m[] = 'break';
+ $m[] = 'case';
+ $m[] = 'catch';
+ $m[] = 'class';
+ $m[] = 'const';
+ $m[] = 'continue';
+ # $m[] = 'declare';
+ $m[] = 'default';
+ $m[] = 'die(';
+ $m[] = 'do';
+ $m[] = 'echo(';
+ $m[] = 'else';
+ $m[] = 'elseif';
+ $m[] = 'empty(';
+ # $m[] = 'enddeclare';
+ $m[] = 'eval(';
+ $m[] = 'exception';
+ $m[] = 'extends';
+ $m[] = 'exit(';
+ $m[] = 'extends';
+ $m[] = 'final';
+ $m[] = 'for (';
+ $m[] = 'foreach (';
+ $m[] = 'function';
+ $m[] = 'global';
+ $m[] = 'if';
+ $m[] = 'implements';
+ $m[] = 'include "';
+ $m[] = 'include_once "';
+ $m[] = 'interface';
+ $m[] = 'isset(';
+ $m[] = 'list(';
+ $m[] = 'new';
+ $m[] = 'or';
+ $m[] = 'print(';
+ $m[] = 'private';
+ $m[] = 'protected';
+ $m[] = 'public';
+ $m[] = 'require "';
+ $m[] = 'require_once "';
+ $m[] = 'return';
+ $m[] = 'static';
+ $m[] = 'switch (';
+ $m[] = 'throw';
+ $m[] = 'try';
+ $m[] = 'unset(';
+ # $m[] = 'use';
+ $m[] = 'var';
+ $m[] = 'while';
+ $m[] = 'xor';
+ $m[] = '__FILE__';
+ $m[] = '__FUNCTION__';
+ $m[] = '__CLASS__';
+ $m[] = '__LINE__';
+ $m[] = '__METHOD__';
+
+ # printf("%s ... %s\n", $str, $pos);
+ return $m;
+}
+
+
diff --git a/framework/3rdParty/PhpShell/php-shell-init.php b/framework/3rdParty/PhpShell/php-shell-init.php
index 20c6af75..6ae8e8fe 100644
--- a/framework/3rdParty/PhpShell/php-shell-init.php
+++ b/framework/3rdParty/PhpShell/php-shell-init.php
@@ -1,88 +1,88 @@
-registerExtensions(array(
- "options" => PHP_Shell_Options::getInstance(), /* the :set command */
-
- "autoload" => new PHP_Shell_Extensions_Autoload(),
- "autoload_debug" => new PHP_Shell_Extensions_AutoloadDebug(),
- "colour" => new PHP_Shell_Extensions_Colour(),
- "exectime" => new PHP_Shell_Extensions_ExecutionTime(),
- "inlinehelp" => new PHP_Shell_Extensions_InlineHelp(),
- "verboseprint" => new PHP_Shell_Extensions_VerbosePrint()
- // "loadscript" => new PHP_Shell_Extensions_LoadScript()
-));
-
+registerExtensions(array(
+ "options" => PHP_Shell_Options::getInstance(), /* the :set command */
+
+ "autoload" => new PHP_Shell_Extensions_Autoload(),
+ "autoload_debug" => new PHP_Shell_Extensions_AutoloadDebug(),
+ "colour" => new PHP_Shell_Extensions_Colour(),
+ "exectime" => new PHP_Shell_Extensions_ExecutionTime(),
+ "inlinehelp" => new PHP_Shell_Extensions_InlineHelp(),
+ "verboseprint" => new PHP_Shell_Extensions_VerbosePrint()
+ // "loadscript" => new PHP_Shell_Extensions_LoadScript()
+));
+
?>
\ No newline at end of file
diff --git a/framework/3rdParty/SafeHtml/HTMLSax3.php b/framework/3rdParty/SafeHtml/HTMLSax3.php
index 1be7aede..80d166e9 100644
--- a/framework/3rdParty/SafeHtml/HTMLSax3.php
+++ b/framework/3rdParty/SafeHtml/HTMLSax3.php
@@ -1,695 +1,695 @@
- Original port from Python |
-// | Authors: Harry Fuecks Port to PEAR + more |
-// | Authors: Many @ Sitepointforums Advanced PHP Forums |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-/**
-* Main parser components
-* @package System.Security.SafeHtml
-* @version $Id$
-*/
-/**
-* Required classes
-*/
-
-require_once(dirname(__FILE__).'/HTMLSax3/States.php');
-require_once(dirname(__FILE__).'/HTMLSax3/Decorators.php');
-
-/**
-* Base State Parser
-* @package System.Security.SafeHtml
-* @access protected
-* @abstract
-*/
-class TSax3_StateParser {
- /**
- * Instance of user front end class to be passed to callbacks
- * @var TSax3
- * @access private
- */
- public $htmlsax;
- /**
- * User defined object for handling elements
- * @var object
- * @access private
- */
- public $handler_object_element;
- /**
- * User defined open tag handler method
- * @var string
- * @access private
- */
- public $handler_method_opening;
- /**
- * User defined close tag handler method
- * @var string
- * @access private
- */
- public $handler_method_closing;
- /**
- * User defined object for handling data in elements
- * @var object
- * @access private
- */
- public $handler_object_data;
- /**
- * User defined data handler method
- * @var string
- * @access private
- */
- public $handler_method_data;
- /**
- * User defined object for handling processing instructions
- * @var object
- * @access private
- */
- public $handler_object_pi;
- /**
- * User defined processing instruction handler method
- * @var string
- * @access private
- */
- public $handler_method_pi;
- /**
- * User defined object for handling JSP/ASP tags
- * @var object
- * @access private
- */
- public $handler_object_jasp;
- /**
- * User defined JSP/ASP handler method
- * @var string
- * @access private
- */
- public $handler_method_jasp;
- /**
- * User defined object for handling XML escapes
- * @var object
- * @access private
- */
- public $handler_object_escape;
- /**
- * User defined XML escape handler method
- * @var string
- * @access private
- */
- public $handler_method_escape;
- /**
- * User defined handler object or NullHandler
- * @var object
- * @access private
- */
- public $handler_default;
- /**
- * Parser options determining parsing behavior
- * @var array
- * @access private
- */
- protected $parser_options = array();
- /**
- * XML document being parsed
- * @var string
- * @access private
- */
- protected $rawtext;
- /**
- * Position in XML document relative to start (0)
- * @var int
- * @access private
- */
- protected $position;
- /**
- * Length of the XML document in characters
- * @var int
- * @access private
- */
- protected $length;
- /**
- * Array of state objects
- * @var array
- * @access private
- */
- protected $State = array();
-
- const TSAX3_STATE_STOP = 0;
- const TSAX3_STATE_START = 1;
- const TSAX3_STATE_TAG = 2;
- const TSAX3_STATE_OPENING_TAG = 3;
- const TSAX3_STATE_CLOSING_TAG = 4;
- const TSAX3_STATE_ESCAPE = 6;
- const TSAX3_STATE_JASP = 7;
- const TSAX3_STATE_PI = 8;
-
- /**
- * Constructs TSax3_StateParser setting up states
- * @var TSax3 instance of user front end class
- * @access protected
- */
- protected function __construct($htmlsax) {
- $this->htmlsax = $htmlsax;
- $this->State[self::TSAX3_STATE_START] = new TSax3_StartingState();
-
- $this->State[self::TSAX3_STATE_CLOSING_TAG] = new TSax3_ClosingTagState();
- $this->State[self::TSAX3_STATE_TAG] = new TSax3_TagState();
- $this->State[self::TSAX3_STATE_OPENING_TAG] = new TSax3_OpeningTagState();
-
- $this->State[self::TSAX3_STATE_PI] = new TSax3_PiState();
- $this->State[self::TSAX3_STATE_JASP] = new TSax3_JaspState();
- $this->State[self::TSAX3_STATE_ESCAPE] = new TSax3_EscapeState();
- }
-
- /**
- * Moves the position back one character
- * @access protected
- * @return void
- */
- function unscanCharacter() {
- $this->position -= 1;
- }
-
- /**
- * Moves the position forward one character
- * @access protected
- * @return void
- */
- function ignoreCharacter() {
- $this->position += 1;
- }
-
- /**
- * Returns the next character from the XML document or void if at end
- * @access protected
- * @return mixed
- */
- function scanCharacter() {
- if ($this->position < $this->length) {
- return $this->rawtext{$this->position++};
- }
- }
-
- /**
- * Returns a string from the current position to the next occurance
- * of the supplied string
- * @param string string to search until
- * @access protected
- * @return string
- */
- function scanUntilString($string) {
- $start = $this->position;
- $this->position = strpos($this->rawtext, $string, $start);
- if ($this->position === FALSE) {
- $this->position = $this->length;
- }
- return substr($this->rawtext, $start, $this->position - $start);
- }
-
- /**
- * Returns a string from the current position until the first instance of
- * one of the characters in the supplied string argument
- * @param string string to search until
- * @access protected
- * @return string
- * @abstract
- */
- function scanUntilCharacters($string) {}
-
- /**
- * Moves the position forward past any whitespace characters
- * @access protected
- * @return void
- * @abstract
- */
- function ignoreWhitespace() {}
-
- /**
- * Begins the parsing operation, setting up any decorators, depending on
- * parse options invoking _parse() to execute parsing
- * @param string XML document to parse
- * @access protected
- * @return void
- */
- function parse($data) {
- if ($this->parser_options['XML_OPTION_TRIM_DATA_NODES']==1) {
- $decorator = new TSax3_Trim(
- $this->handler_object_data,
- $this->handler_method_data);
- $this->handler_object_data =& $decorator;
- $this->handler_method_data = 'trimData';
- }
- if ($this->parser_options['XML_OPTION_CASE_FOLDING']==1) {
- $open_decor = new TSax3_CaseFolding(
- $this->handler_object_element,
- $this->handler_method_opening,
- $this->handler_method_closing);
- $this->handler_object_element =& $open_decor;
- $this->handler_method_opening ='foldOpen';
- $this->handler_method_closing ='foldClose';
- }
- if ($this->parser_options['XML_OPTION_LINEFEED_BREAK']==1) {
- $decorator = new TSax3_Linefeed(
- $this->handler_object_data,
- $this->handler_method_data);
- $this->handler_object_data =& $decorator;
- $this->handler_method_data = 'breakData';
- }
- if ($this->parser_options['XML_OPTION_TAB_BREAK']==1) {
- $decorator = new TSax3_Tab(
- $this->handler_object_data,
- $this->handler_method_data);
- $this->handler_object_data =& $decorator;
- $this->handler_method_data = 'breakData';
- }
- if ($this->parser_options['XML_OPTION_ENTITIES_UNPARSED']==1) {
- $decorator = new TSax3_Entities_Unparsed(
- $this->handler_object_data,
- $this->handler_method_data);
- $this->handler_object_data =& $decorator;
- $this->handler_method_data = 'breakData';
- }
- if ($this->parser_options['XML_OPTION_ENTITIES_PARSED']==1) {
- $decorator = new TSax3_Entities_Parsed(
- $this->handler_object_data,
- $this->handler_method_data);
- $this->handler_object_data =& $decorator;
- $this->handler_method_data = 'breakData';
- }
- // Note switched on by default
- if ($this->parser_options['XML_OPTION_STRIP_ESCAPES']==1) {
- $decorator = new TSax3_Escape_Stripper(
- $this->handler_object_escape,
- $this->handler_method_escape);
- $this->handler_object_escape =& $decorator;
- $this->handler_method_escape = 'strip';
- }
- $this->rawtext = $data;
- $this->length = strlen($data);
- $this->position = 0;
- $this->_parse();
- }
-
- /**
- * Performs the parsing itself, delegating calls to a specific parser
- * state
- * @param constant state object to parse with
- * @access protected
- * @return void
- */
- function _parse($state = self::TSAX3_STATE_START) {
- do {
- $state = $this->State[$state]->parse($this);
- } while ($state != self::TSAX3_STATE_STOP &&
- $this->position < $this->length);
- }
-}
-
-/**
-* Parser for PHP Versions below 4.3.0. Uses a slower parsing mechanism than
-* the equivalent PHP 4.3.0+ subclass of StateParser
-* @package System.Security.SafeHtml
-* @access protected
-* @see TSax3_StateParser_Gtet430
-*/
-class TSax3_StateParser_Lt430 extends TSax3_StateParser {
- /**
- * Constructs TSax3_StateParser_Lt430 defining available
- * parser options
- * @var TSax3 instance of user front end class
- * @access protected
- */
- function __construct(& $htmlsax) {
- parent::__construct($htmlsax);
- $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
- $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
- $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
- $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
- $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
- $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
- $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
- //var_dump($this->parser_options);
- }
-
- /**
- * Returns a string from the current position until the first instance of
- * one of the characters in the supplied string argument
- * @param string string to search until
- * @access protected
- * @return string
- */
- function scanUntilCharacters($string) {
- $startpos = $this->position;
- while ($this->position < $this->length && strpos($string, $this->rawtext{$this->position}) === FALSE) {
- $this->position++;
- }
- return substr($this->rawtext, $startpos, $this->position - $startpos);
- }
-
- /**
- * Moves the position forward past any whitespace characters
- * @access protected
- * @return void
- */
- function ignoreWhitespace() {
- while ($this->position < $this->length &&
- strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {
- $this->position++;
- }
- }
-
- /**
- * Begins the parsing operation, setting up the unparsed XML entities
- * decorator if necessary then delegating further work to parent
- * @param string XML document to parse
- * @access protected
- * @return void
- */
- function parse($data) {
- parent::parse($data);
- }
-}
-
-/**
-* Parser for PHP Versions equal to or greater than 4.3.0. Uses a faster
-* parsing mechanism than the equivalent PHP < 4.3.0 subclass of StateParser
-* @package System.Security.SafeHtml
-* @access protected
-* @see TSax3_StateParser_Lt430
-*/
-class TSax3_StateParser_Gtet430 extends TSax3_StateParser {
- /**
- * Constructs TSax3_StateParser_Gtet430 defining available
- * parser options
- * @var TSax3 instance of user front end class
- * @access protected
- */
- function __construct(& $htmlsax) {
- parent::__construct($htmlsax);
- $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
- $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
- $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
- $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
- $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
- $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
- $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
- }
- /**
- * Returns a string from the current position until the first instance of
- * one of the characters in the supplied string argument.
- * @param string string to search until
- * @access protected
- * @return string
- */
- function scanUntilCharacters($string) {
- $startpos = $this->position;
- $length = strcspn($this->rawtext, $string, $startpos);
- $this->position += $length;
- return substr($this->rawtext, $startpos, $length);
- }
-
- /**
- * Moves the position forward past any whitespace characters
- * @access protected
- * @return void
- */
- function ignoreWhitespace() {
- $this->position += strspn($this->rawtext, " \n\r\t", $this->position);
- }
-
- /**
- * Begins the parsing operation, setting up the parsed and unparsed
- * XML entity decorators if necessary then delegating further work
- * to parent
- * @param string XML document to parse
- * @access protected
- * @return void
- */
- function parse($data) {
- parent::parse($data);
- }
-}
-
-/**
-* Default NullHandler for methods which were not set by user
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_NullHandler {
- /**
- * Generic handler method which does nothing
- * @access protected
- * @return void
- */
- function DoNothing() {
- }
-}
-
-/**
-* User interface class. All user calls should only be made to this class
-* @package System.Security.SafeHtml
-* @access public
-*/
-class TSax3 {
- /**
- * Instance of concrete subclass of TSax3_StateParser
- * @var TSax3_StateParser
- * @access private
- */
- private $state_parser;
-
- /**
- * Constructs TSax3 selecting concrete StateParser subclass
- * depending on PHP version being used as well as setting the default
- * NullHandler for all callbacks
- * Example:
- *
- * $myHandler = & new MyHandler();
- * $parser = new TSax3();
- * $parser->set_object($myHandler);
- * $parser->set_option('XML_OPTION_CASE_FOLDING');
- * $parser->set_element_handler('myOpenHandler','myCloseHandler');
- * $parser->set_data_handler('myDataHandler');
- * $parser->parser($xml);
- *
- * @access public
- */
- function __construct() {
- if (version_compare(phpversion(), '4.3', 'ge')) {
- $this->state_parser = new TSax3_StateParser_Gtet430($this);
- } else {
- $this->state_parser = new TSax3_StateParser_Lt430($this);
- }
- $nullhandler = new TSax3_NullHandler();
- $this->set_object($nullhandler);
- $this->set_element_handler('DoNothing', 'DoNothing');
- $this->set_data_handler('DoNothing');
- $this->set_pi_handler('DoNothing');
- $this->set_jasp_handler('DoNothing');
- $this->set_escape_handler('DoNothing');
- }
-
- /**
- * Sets the user defined handler object. Returns a PEAR Error
- * if supplied argument is not an object.
- * @param object handler object containing SAX callback methods
- * @access public
- * @return mixed
- */
- function set_object(&$object) {
- if ( is_object($object) ) {
- $this->state_parser->handler_default =& $object;
- return true;
- } else {
- require_once('PEAR.php');
- PEAR::raiseError('TSax3::set_object requires '.
- 'an object instance');
- }
- }
-
- /**
- * Sets a parser option. By default all options are switched off.
- * Returns a PEAR Error if option is invalid
- * Available options:
- *
- * - XML_OPTION_TRIM_DATA_NODES: trim whitespace off the beginning
- * and end of data passed to the data handler
- * - XML_OPTION_LINEFEED_BREAK: linefeeds result in additional data
- * handler calls
- * - XML_OPTION_TAB_BREAK: tabs result in additional data handler
- * calls
- * - XML_OPTION_ENTITIES_UNPARSED: XML entities are returned as
- * seperate data handler calls in unparsed form
- * - XML_OPTION_ENTITIES_PARSED: (PHP 4.3.0+ only) XML entities are
- * returned as seperate data handler calls and are parsed with
- * PHP's html_entity_decode() function
- * - XML_OPTION_STRIP_ESCAPES: strips out the -- -- comment markers
- * or CDATA markup inside an XML escape, if found.
- *
- * To get HTMLSax to behave in the same way as the native PHP SAX parser,
- * using it's default state, you need to switch on XML_OPTION_LINEFEED_BREAK,
- * XML_OPTION_ENTITIES_PARSED and XML_OPTION_CASE_FOLDING
- * @param string name of parser option
- * @param int (optional) 1 to switch on, 0 for off
- * @access public
- * @return boolean
- */
- function set_option($name, $value=1) {
- if ( array_key_exists($name,$this->state_parser->parser_options) ) {
- $this->state_parser->parser_options[$name] = $value;
- return true;
- } else {
- require_once('PEAR.php');
- PEAR::raiseError('TSax3::set_option('.$name.') illegal');
- }
- }
-
- /**
- * Sets the data handler method which deals with the contents of XML
- * elements.
- * The handler method must accept two arguments, the first being an
- * instance of TSax3 and the second being the contents of an
- * XML element e.g.
- *
- * function myDataHander(& $parser,$data){}
- *
- * @param string name of method
- * @access public
- * @return void
- * @see set_object
- */
- function set_data_handler($data_method) {
- $this->state_parser->handler_object_data =& $this->state_parser->handler_default;
- $this->state_parser->handler_method_data = $data_method;
- }
-
- /**
- * Sets the open and close tag handlers
- *
The open handler method must accept three arguments; the parser,
- * the tag name and an array of attributes e.g.
- *
- * function myOpenHander(& $parser,$tagname,$attrs=array()){}
- *
- * The close handler method must accept two arguments; the parser and
- * the tag name e.g.
- *
- * function myCloseHander(& $parser,$tagname){}
- *
- * @param string name of open method
- * @param string name of close method
- * @access public
- * @return void
- * @see set_object
- */
- function set_element_handler($opening_method, $closing_method) {
- $this->state_parser->handler_object_element =& $this->state_parser->handler_default;
- $this->state_parser->handler_method_opening = $opening_method;
- $this->state_parser->handler_method_closing = $closing_method;
- }
-
- /**
- * Sets the processing instruction handler method e.g. for PHP open
- * and close tags
- * The handler method must accept three arguments; the parser, the
- * PI target and data inside the PI
- *
- * function myPIHander(& $parser,$target, $data){}
- *
- * @param string name of method
- * @access public
- * @return void
- * @see set_object
- */
- function set_pi_handler($pi_method) {
- $this->state_parser->handler_object_pi =& $this->state_parser->handler_default;
- $this->state_parser->handler_method_pi = $pi_method;
- }
-
- /**
- * Sets the XML escape handler method e.g. for comments and doctype
- * declarations
- * The handler method must accept two arguments; the parser and the
- * contents of the escaped section
- *
- * function myEscapeHander(& $parser, $data){}
- *
- * @param string name of method
- * @access public
- * @return void
- * @see set_object
- */
- function set_escape_handler($escape_method) {
- $this->state_parser->handler_object_escape =& $this->state_parser->handler_default;
- $this->state_parser->handler_method_escape = $escape_method;
- }
-
- /**
- * Sets the JSP/ASP markup handler
- * The handler method must accept two arguments; the parser and
- * body of the JASP tag
- *
- * function myJaspHander(& $parser, $data){}
- *
- * @param string name of method
- * @access public
- * @return void
- * @see set_object
- */
- function set_jasp_handler ($jasp_method) {
- $this->state_parser->handler_object_jasp =& $this->state_parser->handler_default;
- $this->state_parser->handler_method_jasp = $jasp_method;
- }
-
- /**
- * Returns the current string position of the "cursor" inside the XML
- * document
- *
Intended for use from within a user defined handler called
- * via the $parser reference e.g.
- *
- * function myDataHandler(& $parser,$data) {
- * echo( 'Current position: '.$parser->get_current_position() );
- * }
- *
- * @access public
- * @return int
- * @see get_length
- */
- function get_current_position() {
- return $this->state_parser->position;
- }
-
- /**
- * Returns the string length of the XML document being parsed
- * @access public
- * @return int
- */
- function get_length() {
- return $this->state_parser->length;
- }
-
- /**
- * Start parsing some XML
- * @param string XML document
- * @access public
- * @return void
- */
- function parse($data) {
- $this->state_parser->parse($data);
- }
-}
+ Original port from Python |
+// | Authors: Harry Fuecks Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+//
+/**
+* Main parser components
+* @package System.Security.SafeHtml
+* @version $Id$
+*/
+/**
+* Required classes
+*/
+
+require_once(dirname(__FILE__).'/HTMLSax3/States.php');
+require_once(dirname(__FILE__).'/HTMLSax3/Decorators.php');
+
+/**
+* Base State Parser
+* @package System.Security.SafeHtml
+* @access protected
+* @abstract
+*/
+class TSax3_StateParser {
+ /**
+ * Instance of user front end class to be passed to callbacks
+ * @var TSax3
+ * @access private
+ */
+ public $htmlsax;
+ /**
+ * User defined object for handling elements
+ * @var object
+ * @access private
+ */
+ public $handler_object_element;
+ /**
+ * User defined open tag handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_opening;
+ /**
+ * User defined close tag handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_closing;
+ /**
+ * User defined object for handling data in elements
+ * @var object
+ * @access private
+ */
+ public $handler_object_data;
+ /**
+ * User defined data handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_data;
+ /**
+ * User defined object for handling processing instructions
+ * @var object
+ * @access private
+ */
+ public $handler_object_pi;
+ /**
+ * User defined processing instruction handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_pi;
+ /**
+ * User defined object for handling JSP/ASP tags
+ * @var object
+ * @access private
+ */
+ public $handler_object_jasp;
+ /**
+ * User defined JSP/ASP handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_jasp;
+ /**
+ * User defined object for handling XML escapes
+ * @var object
+ * @access private
+ */
+ public $handler_object_escape;
+ /**
+ * User defined XML escape handler method
+ * @var string
+ * @access private
+ */
+ public $handler_method_escape;
+ /**
+ * User defined handler object or NullHandler
+ * @var object
+ * @access private
+ */
+ public $handler_default;
+ /**
+ * Parser options determining parsing behavior
+ * @var array
+ * @access private
+ */
+ protected $parser_options = array();
+ /**
+ * XML document being parsed
+ * @var string
+ * @access private
+ */
+ protected $rawtext;
+ /**
+ * Position in XML document relative to start (0)
+ * @var int
+ * @access private
+ */
+ protected $position;
+ /**
+ * Length of the XML document in characters
+ * @var int
+ * @access private
+ */
+ protected $length;
+ /**
+ * Array of state objects
+ * @var array
+ * @access private
+ */
+ protected $State = array();
+
+ const TSAX3_STATE_STOP = 0;
+ const TSAX3_STATE_START = 1;
+ const TSAX3_STATE_TAG = 2;
+ const TSAX3_STATE_OPENING_TAG = 3;
+ const TSAX3_STATE_CLOSING_TAG = 4;
+ const TSAX3_STATE_ESCAPE = 6;
+ const TSAX3_STATE_JASP = 7;
+ const TSAX3_STATE_PI = 8;
+
+ /**
+ * Constructs TSax3_StateParser setting up states
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ protected function __construct($htmlsax) {
+ $this->htmlsax = $htmlsax;
+ $this->State[self::TSAX3_STATE_START] = new TSax3_StartingState();
+
+ $this->State[self::TSAX3_STATE_CLOSING_TAG] = new TSax3_ClosingTagState();
+ $this->State[self::TSAX3_STATE_TAG] = new TSax3_TagState();
+ $this->State[self::TSAX3_STATE_OPENING_TAG] = new TSax3_OpeningTagState();
+
+ $this->State[self::TSAX3_STATE_PI] = new TSax3_PiState();
+ $this->State[self::TSAX3_STATE_JASP] = new TSax3_JaspState();
+ $this->State[self::TSAX3_STATE_ESCAPE] = new TSax3_EscapeState();
+ }
+
+ /**
+ * Moves the position back one character
+ * @access protected
+ * @return void
+ */
+ function unscanCharacter() {
+ $this->position -= 1;
+ }
+
+ /**
+ * Moves the position forward one character
+ * @access protected
+ * @return void
+ */
+ function ignoreCharacter() {
+ $this->position += 1;
+ }
+
+ /**
+ * Returns the next character from the XML document or void if at end
+ * @access protected
+ * @return mixed
+ */
+ function scanCharacter() {
+ if ($this->position < $this->length) {
+ return $this->rawtext{$this->position++};
+ }
+ }
+
+ /**
+ * Returns a string from the current position to the next occurance
+ * of the supplied string
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilString($string) {
+ $start = $this->position;
+ $this->position = strpos($this->rawtext, $string, $start);
+ if ($this->position === FALSE) {
+ $this->position = $this->length;
+ }
+ return substr($this->rawtext, $start, $this->position - $start);
+ }
+
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument
+ * @param string string to search until
+ * @access protected
+ * @return string
+ * @abstract
+ */
+ function scanUntilCharacters($string) {}
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ * @abstract
+ */
+ function ignoreWhitespace() {}
+
+ /**
+ * Begins the parsing operation, setting up any decorators, depending on
+ * parse options invoking _parse() to execute parsing
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ if ($this->parser_options['XML_OPTION_TRIM_DATA_NODES']==1) {
+ $decorator = new TSax3_Trim(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'trimData';
+ }
+ if ($this->parser_options['XML_OPTION_CASE_FOLDING']==1) {
+ $open_decor = new TSax3_CaseFolding(
+ $this->handler_object_element,
+ $this->handler_method_opening,
+ $this->handler_method_closing);
+ $this->handler_object_element =& $open_decor;
+ $this->handler_method_opening ='foldOpen';
+ $this->handler_method_closing ='foldClose';
+ }
+ if ($this->parser_options['XML_OPTION_LINEFEED_BREAK']==1) {
+ $decorator = new TSax3_Linefeed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_TAB_BREAK']==1) {
+ $decorator = new TSax3_Tab(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_ENTITIES_UNPARSED']==1) {
+ $decorator = new TSax3_Entities_Unparsed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ if ($this->parser_options['XML_OPTION_ENTITIES_PARSED']==1) {
+ $decorator = new TSax3_Entities_Parsed(
+ $this->handler_object_data,
+ $this->handler_method_data);
+ $this->handler_object_data =& $decorator;
+ $this->handler_method_data = 'breakData';
+ }
+ // Note switched on by default
+ if ($this->parser_options['XML_OPTION_STRIP_ESCAPES']==1) {
+ $decorator = new TSax3_Escape_Stripper(
+ $this->handler_object_escape,
+ $this->handler_method_escape);
+ $this->handler_object_escape =& $decorator;
+ $this->handler_method_escape = 'strip';
+ }
+ $this->rawtext = $data;
+ $this->length = strlen($data);
+ $this->position = 0;
+ $this->_parse();
+ }
+
+ /**
+ * Performs the parsing itself, delegating calls to a specific parser
+ * state
+ * @param constant state object to parse with
+ * @access protected
+ * @return void
+ */
+ function _parse($state = self::TSAX3_STATE_START) {
+ do {
+ $state = $this->State[$state]->parse($this);
+ } while ($state != self::TSAX3_STATE_STOP &&
+ $this->position < $this->length);
+ }
+}
+
+/**
+* Parser for PHP Versions below 4.3.0. Uses a slower parsing mechanism than
+* the equivalent PHP 4.3.0+ subclass of StateParser
+* @package System.Security.SafeHtml
+* @access protected
+* @see TSax3_StateParser_Gtet430
+*/
+class TSax3_StateParser_Lt430 extends TSax3_StateParser {
+ /**
+ * Constructs TSax3_StateParser_Lt430 defining available
+ * parser options
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ function __construct(& $htmlsax) {
+ parent::__construct($htmlsax);
+ $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
+ $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
+ $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
+ $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
+ //var_dump($this->parser_options);
+ }
+
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilCharacters($string) {
+ $startpos = $this->position;
+ while ($this->position < $this->length && strpos($string, $this->rawtext{$this->position}) === FALSE) {
+ $this->position++;
+ }
+ return substr($this->rawtext, $startpos, $this->position - $startpos);
+ }
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ */
+ function ignoreWhitespace() {
+ while ($this->position < $this->length &&
+ strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {
+ $this->position++;
+ }
+ }
+
+ /**
+ * Begins the parsing operation, setting up the unparsed XML entities
+ * decorator if necessary then delegating further work to parent
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ parent::parse($data);
+ }
+}
+
+/**
+* Parser for PHP Versions equal to or greater than 4.3.0. Uses a faster
+* parsing mechanism than the equivalent PHP < 4.3.0 subclass of StateParser
+* @package System.Security.SafeHtml
+* @access protected
+* @see TSax3_StateParser_Lt430
+*/
+class TSax3_StateParser_Gtet430 extends TSax3_StateParser {
+ /**
+ * Constructs TSax3_StateParser_Gtet430 defining available
+ * parser options
+ * @var TSax3 instance of user front end class
+ * @access protected
+ */
+ function __construct(& $htmlsax) {
+ parent::__construct($htmlsax);
+ $this->parser_options['XML_OPTION_TRIM_DATA_NODES'] = 0;
+ $this->parser_options['XML_OPTION_CASE_FOLDING'] = 0;
+ $this->parser_options['XML_OPTION_LINEFEED_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_TAB_BREAK'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_PARSED'] = 0;
+ $this->parser_options['XML_OPTION_ENTITIES_UNPARSED'] = 0;
+ $this->parser_options['XML_OPTION_STRIP_ESCAPES'] = 0;
+ }
+ /**
+ * Returns a string from the current position until the first instance of
+ * one of the characters in the supplied string argument.
+ * @param string string to search until
+ * @access protected
+ * @return string
+ */
+ function scanUntilCharacters($string) {
+ $startpos = $this->position;
+ $length = strcspn($this->rawtext, $string, $startpos);
+ $this->position += $length;
+ return substr($this->rawtext, $startpos, $length);
+ }
+
+ /**
+ * Moves the position forward past any whitespace characters
+ * @access protected
+ * @return void
+ */
+ function ignoreWhitespace() {
+ $this->position += strspn($this->rawtext, " \n\r\t", $this->position);
+ }
+
+ /**
+ * Begins the parsing operation, setting up the parsed and unparsed
+ * XML entity decorators if necessary then delegating further work
+ * to parent
+ * @param string XML document to parse
+ * @access protected
+ * @return void
+ */
+ function parse($data) {
+ parent::parse($data);
+ }
+}
+
+/**
+* Default NullHandler for methods which were not set by user
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_NullHandler {
+ /**
+ * Generic handler method which does nothing
+ * @access protected
+ * @return void
+ */
+ function DoNothing() {
+ }
+}
+
+/**
+* User interface class. All user calls should only be made to this class
+* @package System.Security.SafeHtml
+* @access public
+*/
+class TSax3 {
+ /**
+ * Instance of concrete subclass of TSax3_StateParser
+ * @var TSax3_StateParser
+ * @access private
+ */
+ private $state_parser;
+
+ /**
+ * Constructs TSax3 selecting concrete StateParser subclass
+ * depending on PHP version being used as well as setting the default
+ * NullHandler for all callbacks
+ * Example:
+ *
+ * $myHandler = & new MyHandler();
+ * $parser = new TSax3();
+ * $parser->set_object($myHandler);
+ * $parser->set_option('XML_OPTION_CASE_FOLDING');
+ * $parser->set_element_handler('myOpenHandler','myCloseHandler');
+ * $parser->set_data_handler('myDataHandler');
+ * $parser->parser($xml);
+ *
+ * @access public
+ */
+ function __construct() {
+ if (version_compare(phpversion(), '4.3', 'ge')) {
+ $this->state_parser = new TSax3_StateParser_Gtet430($this);
+ } else {
+ $this->state_parser = new TSax3_StateParser_Lt430($this);
+ }
+ $nullhandler = new TSax3_NullHandler();
+ $this->set_object($nullhandler);
+ $this->set_element_handler('DoNothing', 'DoNothing');
+ $this->set_data_handler('DoNothing');
+ $this->set_pi_handler('DoNothing');
+ $this->set_jasp_handler('DoNothing');
+ $this->set_escape_handler('DoNothing');
+ }
+
+ /**
+ * Sets the user defined handler object. Returns a PEAR Error
+ * if supplied argument is not an object.
+ * @param object handler object containing SAX callback methods
+ * @access public
+ * @return mixed
+ */
+ function set_object(&$object) {
+ if ( is_object($object) ) {
+ $this->state_parser->handler_default =& $object;
+ return true;
+ } else {
+ require_once('PEAR.php');
+ PEAR::raiseError('TSax3::set_object requires '.
+ 'an object instance');
+ }
+ }
+
+ /**
+ * Sets a parser option. By default all options are switched off.
+ * Returns a PEAR Error if option is invalid
+ * Available options:
+ *
+ * - XML_OPTION_TRIM_DATA_NODES: trim whitespace off the beginning
+ * and end of data passed to the data handler
+ * - XML_OPTION_LINEFEED_BREAK: linefeeds result in additional data
+ * handler calls
+ * - XML_OPTION_TAB_BREAK: tabs result in additional data handler
+ * calls
+ * - XML_OPTION_ENTITIES_UNPARSED: XML entities are returned as
+ * seperate data handler calls in unparsed form
+ * - XML_OPTION_ENTITIES_PARSED: (PHP 4.3.0+ only) XML entities are
+ * returned as seperate data handler calls and are parsed with
+ * PHP's html_entity_decode() function
+ * - XML_OPTION_STRIP_ESCAPES: strips out the -- -- comment markers
+ * or CDATA markup inside an XML escape, if found.
+ *
+ * To get HTMLSax to behave in the same way as the native PHP SAX parser,
+ * using it's default state, you need to switch on XML_OPTION_LINEFEED_BREAK,
+ * XML_OPTION_ENTITIES_PARSED and XML_OPTION_CASE_FOLDING
+ * @param string name of parser option
+ * @param int (optional) 1 to switch on, 0 for off
+ * @access public
+ * @return boolean
+ */
+ function set_option($name, $value=1) {
+ if ( array_key_exists($name,$this->state_parser->parser_options) ) {
+ $this->state_parser->parser_options[$name] = $value;
+ return true;
+ } else {
+ require_once('PEAR.php');
+ PEAR::raiseError('TSax3::set_option('.$name.') illegal');
+ }
+ }
+
+ /**
+ * Sets the data handler method which deals with the contents of XML
+ * elements.
+ * The handler method must accept two arguments, the first being an
+ * instance of TSax3 and the second being the contents of an
+ * XML element e.g.
+ *
+ * function myDataHander(& $parser,$data){}
+ *
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_data_handler($data_method) {
+ $this->state_parser->handler_object_data =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_data = $data_method;
+ }
+
+ /**
+ * Sets the open and close tag handlers
+ *
The open handler method must accept three arguments; the parser,
+ * the tag name and an array of attributes e.g.
+ *
+ * function myOpenHander(& $parser,$tagname,$attrs=array()){}
+ *
+ * The close handler method must accept two arguments; the parser and
+ * the tag name e.g.
+ *
+ * function myCloseHander(& $parser,$tagname){}
+ *
+ * @param string name of open method
+ * @param string name of close method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_element_handler($opening_method, $closing_method) {
+ $this->state_parser->handler_object_element =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_opening = $opening_method;
+ $this->state_parser->handler_method_closing = $closing_method;
+ }
+
+ /**
+ * Sets the processing instruction handler method e.g. for PHP open
+ * and close tags
+ * The handler method must accept three arguments; the parser, the
+ * PI target and data inside the PI
+ *
+ * function myPIHander(& $parser,$target, $data){}
+ *
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_pi_handler($pi_method) {
+ $this->state_parser->handler_object_pi =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_pi = $pi_method;
+ }
+
+ /**
+ * Sets the XML escape handler method e.g. for comments and doctype
+ * declarations
+ * The handler method must accept two arguments; the parser and the
+ * contents of the escaped section
+ *
+ * function myEscapeHander(& $parser, $data){}
+ *
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_escape_handler($escape_method) {
+ $this->state_parser->handler_object_escape =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_escape = $escape_method;
+ }
+
+ /**
+ * Sets the JSP/ASP markup handler
+ * The handler method must accept two arguments; the parser and
+ * body of the JASP tag
+ *
+ * function myJaspHander(& $parser, $data){}
+ *
+ * @param string name of method
+ * @access public
+ * @return void
+ * @see set_object
+ */
+ function set_jasp_handler ($jasp_method) {
+ $this->state_parser->handler_object_jasp =& $this->state_parser->handler_default;
+ $this->state_parser->handler_method_jasp = $jasp_method;
+ }
+
+ /**
+ * Returns the current string position of the "cursor" inside the XML
+ * document
+ *
Intended for use from within a user defined handler called
+ * via the $parser reference e.g.
+ *
+ * function myDataHandler(& $parser,$data) {
+ * echo( 'Current position: '.$parser->get_current_position() );
+ * }
+ *
+ * @access public
+ * @return int
+ * @see get_length
+ */
+ function get_current_position() {
+ return $this->state_parser->position;
+ }
+
+ /**
+ * Returns the string length of the XML document being parsed
+ * @access public
+ * @return int
+ */
+ function get_length() {
+ return $this->state_parser->length;
+ }
+
+ /**
+ * Start parsing some XML
+ * @param string XML document
+ * @access public
+ * @return void
+ */
+ function parse($data) {
+ $this->state_parser->parse($data);
+ }
+}
?>
\ No newline at end of file
diff --git a/framework/3rdParty/SafeHtml/HTMLSax3/Decorators.php b/framework/3rdParty/SafeHtml/HTMLSax3/Decorators.php
index ac82d073..2e2b6590 100644
--- a/framework/3rdParty/SafeHtml/HTMLSax3/Decorators.php
+++ b/framework/3rdParty/SafeHtml/HTMLSax3/Decorators.php
@@ -1,363 +1,363 @@
- Original port from Python |
-// | Authors: Harry Fuecks Port to PEAR + more |
-// | Authors: Many @ Sitepointforums Advanced PHP Forums |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-/**
-* Decorators for dealing with parser options
-* @package System.Security.SafeHtml
-* @version $Id$
-* @see TSax3::set_option
-*/
-/**
-* Trims the contents of element data from whitespace at start and end
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Trim {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_Trim
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Trims the data
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function trimData(&$parser, $data) {
- $data = trim($data);
- if ($data != '') {
- $this->orig_obj->{$this->orig_method}($parser, $data);
- }
- }
-}
-/**
-* Coverts tag names to upper case
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_CaseFolding {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original open handler method
- * @var string
- * @access private
- */
- private $orig_open_method;
- /**
- * Original close handler method
- * @var string
- * @access private
- */
- private $orig_close_method;
- /**
- * Constructs TSax3_CaseFolding
- * @param object handler object being decorated
- * @param string original open handler method
- * @param string original close handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_open_method, $orig_close_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_open_method = $orig_open_method;
- $this->orig_close_method = $orig_close_method;
- }
- /**
- * Folds up open tag callbacks
- * @param TSax3
- * @param string tag name
- * @param array tag attributes
- * @access protected
- */
- function foldOpen(&$parser, $tag, $attrs=array(), $empty = FALSE) {
- $this->orig_obj->{$this->orig_open_method}($parser, strtoupper($tag), $attrs, $empty);
- }
- /**
- * Folds up close tag callbacks
- * @param TSax3
- * @param string tag name
- * @access protected
- */
- function foldClose(&$parser, $tag, $empty = FALSE) {
- $this->orig_obj->{$this->orig_close_method}($parser, strtoupper($tag), $empty);
- }
-}
-/**
-* Breaks up data by linefeed characters, resulting in additional
-* calls to the data handler
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Linefeed {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_LineFeed
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Breaks the data up by linefeeds
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function breakData(&$parser, $data) {
- $data = explode("\n",$data);
- foreach ( $data as $chunk ) {
- $this->orig_obj->{$this->orig_method}($parser, $chunk);
- }
- }
-}
-/**
-* Breaks up data by tab characters, resulting in additional
-* calls to the data handler
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Tab {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_Tab
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Breaks the data up by linefeeds
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function breakData(&$parser, $data) {
- $data = explode("\t",$data);
- foreach ( $data as $chunk ) {
- $this->orig_obj->{$this->orig_method}($this, $chunk);
- }
- }
-}
-/**
-* Breaks up data by XML entities and parses them with html_entity_decode(),
-* resulting in additional calls to the data handler
-* Requires PHP 4.3.0+
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Entities_Parsed {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_Entities_Parsed
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Breaks the data up by XML entities
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function breakData(&$parser, $data) {
- $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
- foreach ( $data as $chunk ) {
- $chunk = html_entity_decode($chunk,ENT_NOQUOTES);
- $this->orig_obj->{$this->orig_method}($this, $chunk);
- }
- }
-}
-/**
-* Compatibility with older PHP versions
-*/
-if (version_compare(phpversion(), '4.3', '<') && !function_exists('html_entity_decode') ) {
- function html_entity_decode($str, $style=ENT_NOQUOTES) {
- return strtr($str,
- array_flip(get_html_translation_table(HTML_ENTITIES,$style)));
- }
-}
-/**
-* Breaks up data by XML entities but leaves them unparsed,
-* resulting in additional calls to the data handler
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Entities_Unparsed {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_Entities_Unparsed
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Breaks the data up by XML entities
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function breakData(&$parser, $data) {
- $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
- foreach ( $data as $chunk ) {
- $this->orig_obj->{$this->orig_method}($this, $chunk);
- }
- }
-}
-
-/**
-* Strips the HTML comment markers or CDATA sections from an escape.
-* If XML_OPTIONS_FULL_ESCAPES is on, this decorator is not used.
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_Escape_Stripper {
- /**
- * Original handler object
- * @var object
- * @access private
- */
- private $orig_obj;
- /**
- * Original handler method
- * @var string
- * @access private
- */
- private $orig_method;
- /**
- * Constructs TSax3_Entities_Unparsed
- * @param object handler object being decorated
- * @param string original handler method
- * @access protected
- */
- function __construct(&$orig_obj, $orig_method) {
- $this->orig_obj =& $orig_obj;
- $this->orig_method = $orig_method;
- }
- /**
- * Breaks the data up by XML entities
- * @param TSax3
- * @param string element data
- * @access protected
- */
- function strip(&$parser, $data) {
- // Check for HTML comments first
- if ( substr($data,0,2) == '--' ) {
- $patterns = array(
- '/^\-\-/', // Opening comment: --
- '/\-\-$/', // Closing comment: --
- );
- $data = preg_replace($patterns,'',$data);
-
- // Check for XML CDATA sections (note: don't do both!)
- } else if ( substr($data,0,1) == '[' ) {
- $patterns = array(
- '/^\[.*CDATA.*\[/s', // Opening CDATA
- '/\].*\]$/s', // Closing CDATA
- );
- $data = preg_replace($patterns,'',$data);
- }
-
- $this->orig_obj->{$this->orig_method}($this, $data);
- }
-}
+ Original port from Python |
+// | Authors: Harry Fuecks Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+//
+/**
+* Decorators for dealing with parser options
+* @package System.Security.SafeHtml
+* @version $Id$
+* @see TSax3::set_option
+*/
+/**
+* Trims the contents of element data from whitespace at start and end
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Trim {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Trim
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Trims the data
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function trimData(&$parser, $data) {
+ $data = trim($data);
+ if ($data != '') {
+ $this->orig_obj->{$this->orig_method}($parser, $data);
+ }
+ }
+}
+/**
+* Coverts tag names to upper case
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_CaseFolding {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original open handler method
+ * @var string
+ * @access private
+ */
+ private $orig_open_method;
+ /**
+ * Original close handler method
+ * @var string
+ * @access private
+ */
+ private $orig_close_method;
+ /**
+ * Constructs TSax3_CaseFolding
+ * @param object handler object being decorated
+ * @param string original open handler method
+ * @param string original close handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_open_method, $orig_close_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_open_method = $orig_open_method;
+ $this->orig_close_method = $orig_close_method;
+ }
+ /**
+ * Folds up open tag callbacks
+ * @param TSax3
+ * @param string tag name
+ * @param array tag attributes
+ * @access protected
+ */
+ function foldOpen(&$parser, $tag, $attrs=array(), $empty = FALSE) {
+ $this->orig_obj->{$this->orig_open_method}($parser, strtoupper($tag), $attrs, $empty);
+ }
+ /**
+ * Folds up close tag callbacks
+ * @param TSax3
+ * @param string tag name
+ * @access protected
+ */
+ function foldClose(&$parser, $tag, $empty = FALSE) {
+ $this->orig_obj->{$this->orig_close_method}($parser, strtoupper($tag), $empty);
+ }
+}
+/**
+* Breaks up data by linefeed characters, resulting in additional
+* calls to the data handler
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Linefeed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_LineFeed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by linefeeds
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = explode("\n",$data);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($parser, $chunk);
+ }
+ }
+}
+/**
+* Breaks up data by tab characters, resulting in additional
+* calls to the data handler
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Tab {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Tab
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by linefeeds
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = explode("\t",$data);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+/**
+* Breaks up data by XML entities and parses them with html_entity_decode(),
+* resulting in additional calls to the data handler
+* Requires PHP 4.3.0+
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Entities_Parsed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Parsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ foreach ( $data as $chunk ) {
+ $chunk = html_entity_decode($chunk,ENT_NOQUOTES);
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+/**
+* Compatibility with older PHP versions
+*/
+if (version_compare(phpversion(), '4.3', '<') && !function_exists('html_entity_decode') ) {
+ function html_entity_decode($str, $style=ENT_NOQUOTES) {
+ return strtr($str,
+ array_flip(get_html_translation_table(HTML_ENTITIES,$style)));
+ }
+}
+/**
+* Breaks up data by XML entities but leaves them unparsed,
+* resulting in additional calls to the data handler
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Entities_Unparsed {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Unparsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function breakData(&$parser, $data) {
+ $data = preg_split('/(&.+?;)/',$data,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ foreach ( $data as $chunk ) {
+ $this->orig_obj->{$this->orig_method}($this, $chunk);
+ }
+ }
+}
+
+/**
+* Strips the HTML comment markers or CDATA sections from an escape.
+* If XML_OPTIONS_FULL_ESCAPES is on, this decorator is not used.
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_Escape_Stripper {
+ /**
+ * Original handler object
+ * @var object
+ * @access private
+ */
+ private $orig_obj;
+ /**
+ * Original handler method
+ * @var string
+ * @access private
+ */
+ private $orig_method;
+ /**
+ * Constructs TSax3_Entities_Unparsed
+ * @param object handler object being decorated
+ * @param string original handler method
+ * @access protected
+ */
+ function __construct(&$orig_obj, $orig_method) {
+ $this->orig_obj =& $orig_obj;
+ $this->orig_method = $orig_method;
+ }
+ /**
+ * Breaks the data up by XML entities
+ * @param TSax3
+ * @param string element data
+ * @access protected
+ */
+ function strip(&$parser, $data) {
+ // Check for HTML comments first
+ if ( substr($data,0,2) == '--' ) {
+ $patterns = array(
+ '/^\-\-/', // Opening comment: --
+ '/\-\-$/', // Closing comment: --
+ );
+ $data = preg_replace($patterns,'',$data);
+
+ // Check for XML CDATA sections (note: don't do both!)
+ } else if ( substr($data,0,1) == '[' ) {
+ $patterns = array(
+ '/^\[.*CDATA.*\[/s', // Opening CDATA
+ '/\].*\]$/s', // Closing CDATA
+ );
+ $data = preg_replace($patterns,'',$data);
+ }
+
+ $this->orig_obj->{$this->orig_method}($this, $data);
+ }
+}
?>
\ No newline at end of file
diff --git a/framework/3rdParty/SafeHtml/HTMLSax3/States.php b/framework/3rdParty/SafeHtml/HTMLSax3/States.php
index 6dfead17..7d7b9b34 100644
--- a/framework/3rdParty/SafeHtml/HTMLSax3/States.php
+++ b/framework/3rdParty/SafeHtml/HTMLSax3/States.php
@@ -1,288 +1,288 @@
- Original port from Python |
-// | Authors: Harry Fuecks Port to PEAR + more |
-// | Authors: Many @ Sitepointforums Advanced PHP Forums |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
-/**
-* Parsing states.
-* @package System.Security.SafeHtml
-* @version $Id$
-*/
-/**
-* Define parser states
-*/
-/*define('TSAX3_STATE_STOP', 0);
-define('TSAX3_STATE_START', 1);
-define('TSAX3_STATE_TAG', 2);
-define('TSAX3_STATE_OPENING_TAG', 3);
-define('TSAX3_STATE_CLOSING_TAG', 4);
-define('TSAX3_STATE_ESCAPE', 6);
-define('TSAX3_STATE_JASP', 7);
-define('TSAX3_STATE_PI', 8);
-*/
-/**
-* StartingState searches for the start of any XML tag
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_StartingState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_TAG
- * @access protected
- */
- function parse(&$context) {
- $data = $context->scanUntilString('<');
- if ($data != '') {
- $context->handler_object_data->
- {$context->handler_method_data}($context->htmlsax, $data);
- }
- $context->IgnoreCharacter();
- return TSax3_StateParser::TSAX3_STATE_TAG;
- }
-}
-/**
-* Decides which state to move one from after StartingState
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_TagState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant the next state to move into
- * @access protected
- */
- function parse(&$context) {
- switch($context->ScanCharacter()) {
- case '/':
- return TSax3_StateParser::TSAX3_STATE_CLOSING_TAG;
- break;
- case '?':
- return TSax3_StateParser::TSAX3_STATE_PI;
- break;
- case '%':
- return TSax3_StateParser::TSAX3_STATE_JASP;
- break;
- case '!':
- return TSax3_StateParser::TSAX3_STATE_ESCAPE;
- break;
- default:
- $context->unscanCharacter();
- return TSax3_StateParser::TSAX3_STATE_OPENING_TAG;
- }
- }
-}
-/**
-* Dealing with closing XML tags
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_ClosingTagState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_START
- * @access protected
- */
- function parse(&$context) {
- $tag = $context->scanUntilCharacters('/>');
- if ($tag != '') {
- $char = $context->scanCharacter();
- if ($char == '/') {
- $char = $context->scanCharacter();
- if ($char != '>') {
- $context->unscanCharacter();
- }
- }
- $context->handler_object_element->
- {$context->handler_method_closing}($context->htmlsax, $tag, FALSE);
- }
- return TSax3_StateParser::TSAX3_STATE_START;
- }
-}
-/**
-* Dealing with opening XML tags
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_OpeningTagState {
- /**
- * Handles attributes
- * @param string attribute name
- * @param string attribute value
- * @return void
- * @access protected
- * @see TSax3_AttributeStartState
- */
- function parseAttributes(&$context) {
- $Attributes = array();
-
- $context->ignoreWhitespace();
- $attributename = $context->scanUntilCharacters("=/> \n\r\t");
- while ($attributename != '') {
- $attributevalue = NULL;
- $context->ignoreWhitespace();
- $char = $context->scanCharacter();
- if ($char == '=') {
- $context->ignoreWhitespace();
- $char = $context->ScanCharacter();
- if ($char == '"') {
- $attributevalue= $context->scanUntilString('"');
- $context->IgnoreCharacter();
- } else if ($char == "'") {
- $attributevalue = $context->scanUntilString("'");
- $context->IgnoreCharacter();
- } else {
- $context->unscanCharacter();
- $attributevalue =
- $context->scanUntilCharacters("> \n\r\t");
- }
- } else if ($char !== NULL) {
- $attributevalue = NULL;
- $context->unscanCharacter();
- }
- $Attributes[$attributename] = $attributevalue;
-
- $context->ignoreWhitespace();
- $attributename = $context->scanUntilCharacters("=/> \n\r\t");
- }
- return $Attributes;
- }
-
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_START
- * @access protected
- */
- function parse(&$context) {
- $tag = $context->scanUntilCharacters("/> \n\r\t");
- if ($tag != '') {
- $this->attrs = array();
- $Attributes = $this->parseAttributes($context);
- $char = $context->scanCharacter();
- if ($char == '/') {
- $char = $context->scanCharacter();
- if ($char != '>') {
- $context->unscanCharacter();
- }
- $context->handler_object_element->
- {$context->handler_method_opening}($context->htmlsax, $tag,
- $Attributes, TRUE);
- $context->handler_object_element->
- {$context->handler_method_closing}($context->htmlsax, $tag,
- TRUE);
- } else {
- $context->handler_object_element->
- {$context->handler_method_opening}($context->htmlsax, $tag,
- $Attributes, FALSE);
- }
- }
- return TSax3_StateParser::TSAX3_STATE_START;
- }
-}
-
-/**
-* Deals with XML escapes handling comments and CDATA correctly
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_EscapeState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_START
- * @access protected
- */
- function parse(&$context) {
- $char = $context->ScanCharacter();
- if ($char == '-') {
- $char = $context->ScanCharacter();
- if ($char == '-') {
- $context->unscanCharacter();
- $context->unscanCharacter();
- $text = $context->scanUntilString('-->');
- $text .= $context->scanCharacter();
- $text .= $context->scanCharacter();
- } else {
- $context->unscanCharacter();
- $text = $context->scanUntilString('>');
- }
- } else if ( $char == '[') {
- $context->unscanCharacter();
- $text = $context->scanUntilString(']>');
- $text.= $context->scanCharacter();
- } else {
- $context->unscanCharacter();
- $text = $context->scanUntilString('>');
- }
-
- $context->IgnoreCharacter();
- if ($text != '') {
- $context->handler_object_escape->
- {$context->handler_method_escape}($context->htmlsax, $text);
- }
- return TSax3_StateParser::TSAX3_STATE_START;
- }
-}
-/**
-* Deals with JASP/ASP markup
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_JaspState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_START
- * @access protected
- */
- function parse(&$context) {
- $text = $context->scanUntilString('%>');
- if ($text != '') {
- $context->handler_object_jasp->
- {$context->handler_method_jasp}($context->htmlsax, $text);
- }
- $context->IgnoreCharacter();
- $context->IgnoreCharacter();
- return TSax3_StateParser::TSAX3_STATE_START;
- }
-}
-/**
-* Deals with XML processing instructions
-* @package System.Security.SafeHtml
-* @access protected
-*/
-class TSax3_PiState {
- /**
- * @param TSax3_StateParser subclass
- * @return constant TSAX3_STATE_START
- * @access protected
- */
- function parse(&$context) {
- $target = $context->scanUntilCharacters(" \n\r\t");
- $data = $context->scanUntilString('?>');
- if ($data != '') {
- $context->handler_object_pi->
- {$context->handler_method_pi}($context->htmlsax, $target, $data);
- }
- $context->IgnoreCharacter();
- $context->IgnoreCharacter();
- return TSax3_StateParser::TSAX3_STATE_START;
- }
-}
+ Original port from Python |
+// | Authors: Harry Fuecks Port to PEAR + more |
+// | Authors: Many @ Sitepointforums Advanced PHP Forums |
+// +----------------------------------------------------------------------+
+//
+// $Id$
+//
+/**
+* Parsing states.
+* @package System.Security.SafeHtml
+* @version $Id$
+*/
+/**
+* Define parser states
+*/
+/*define('TSAX3_STATE_STOP', 0);
+define('TSAX3_STATE_START', 1);
+define('TSAX3_STATE_TAG', 2);
+define('TSAX3_STATE_OPENING_TAG', 3);
+define('TSAX3_STATE_CLOSING_TAG', 4);
+define('TSAX3_STATE_ESCAPE', 6);
+define('TSAX3_STATE_JASP', 7);
+define('TSAX3_STATE_PI', 8);
+*/
+/**
+* StartingState searches for the start of any XML tag
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_StartingState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_TAG
+ * @access protected
+ */
+ function parse(&$context) {
+ $data = $context->scanUntilString('<');
+ if ($data != '') {
+ $context->handler_object_data->
+ {$context->handler_method_data}($context->htmlsax, $data);
+ }
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_TAG;
+ }
+}
+/**
+* Decides which state to move one from after StartingState
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_TagState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant the next state to move into
+ * @access protected
+ */
+ function parse(&$context) {
+ switch($context->ScanCharacter()) {
+ case '/':
+ return TSax3_StateParser::TSAX3_STATE_CLOSING_TAG;
+ break;
+ case '?':
+ return TSax3_StateParser::TSAX3_STATE_PI;
+ break;
+ case '%':
+ return TSax3_StateParser::TSAX3_STATE_JASP;
+ break;
+ case '!':
+ return TSax3_StateParser::TSAX3_STATE_ESCAPE;
+ break;
+ default:
+ $context->unscanCharacter();
+ return TSax3_StateParser::TSAX3_STATE_OPENING_TAG;
+ }
+ }
+}
+/**
+* Dealing with closing XML tags
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_ClosingTagState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $tag = $context->scanUntilCharacters('/>');
+ if ($tag != '') {
+ $char = $context->scanCharacter();
+ if ($char == '/') {
+ $char = $context->scanCharacter();
+ if ($char != '>') {
+ $context->unscanCharacter();
+ }
+ }
+ $context->handler_object_element->
+ {$context->handler_method_closing}($context->htmlsax, $tag, FALSE);
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Dealing with opening XML tags
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_OpeningTagState {
+ /**
+ * Handles attributes
+ * @param string attribute name
+ * @param string attribute value
+ * @return void
+ * @access protected
+ * @see TSax3_AttributeStartState
+ */
+ function parseAttributes(&$context) {
+ $Attributes = array();
+
+ $context->ignoreWhitespace();
+ $attributename = $context->scanUntilCharacters("=/> \n\r\t");
+ while ($attributename != '') {
+ $attributevalue = NULL;
+ $context->ignoreWhitespace();
+ $char = $context->scanCharacter();
+ if ($char == '=') {
+ $context->ignoreWhitespace();
+ $char = $context->ScanCharacter();
+ if ($char == '"') {
+ $attributevalue= $context->scanUntilString('"');
+ $context->IgnoreCharacter();
+ } else if ($char == "'") {
+ $attributevalue = $context->scanUntilString("'");
+ $context->IgnoreCharacter();
+ } else {
+ $context->unscanCharacter();
+ $attributevalue =
+ $context->scanUntilCharacters("> \n\r\t");
+ }
+ } else if ($char !== NULL) {
+ $attributevalue = NULL;
+ $context->unscanCharacter();
+ }
+ $Attributes[$attributename] = $attributevalue;
+
+ $context->ignoreWhitespace();
+ $attributename = $context->scanUntilCharacters("=/> \n\r\t");
+ }
+ return $Attributes;
+ }
+
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $tag = $context->scanUntilCharacters("/> \n\r\t");
+ if ($tag != '') {
+ $this->attrs = array();
+ $Attributes = $this->parseAttributes($context);
+ $char = $context->scanCharacter();
+ if ($char == '/') {
+ $char = $context->scanCharacter();
+ if ($char != '>') {
+ $context->unscanCharacter();
+ }
+ $context->handler_object_element->
+ {$context->handler_method_opening}($context->htmlsax, $tag,
+ $Attributes, TRUE);
+ $context->handler_object_element->
+ {$context->handler_method_closing}($context->htmlsax, $tag,
+ TRUE);
+ } else {
+ $context->handler_object_element->
+ {$context->handler_method_opening}($context->htmlsax, $tag,
+ $Attributes, FALSE);
+ }
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+
+/**
+* Deals with XML escapes handling comments and CDATA correctly
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_EscapeState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $char = $context->ScanCharacter();
+ if ($char == '-') {
+ $char = $context->ScanCharacter();
+ if ($char == '-') {
+ $context->unscanCharacter();
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('-->');
+ $text .= $context->scanCharacter();
+ $text .= $context->scanCharacter();
+ } else {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('>');
+ }
+ } else if ( $char == '[') {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString(']>');
+ $text.= $context->scanCharacter();
+ } else {
+ $context->unscanCharacter();
+ $text = $context->scanUntilString('>');
+ }
+
+ $context->IgnoreCharacter();
+ if ($text != '') {
+ $context->handler_object_escape->
+ {$context->handler_method_escape}($context->htmlsax, $text);
+ }
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Deals with JASP/ASP markup
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_JaspState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $text = $context->scanUntilString('%>');
+ if ($text != '') {
+ $context->handler_object_jasp->
+ {$context->handler_method_jasp}($context->htmlsax, $text);
+ }
+ $context->IgnoreCharacter();
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
+/**
+* Deals with XML processing instructions
+* @package System.Security.SafeHtml
+* @access protected
+*/
+class TSax3_PiState {
+ /**
+ * @param TSax3_StateParser subclass
+ * @return constant TSAX3_STATE_START
+ * @access protected
+ */
+ function parse(&$context) {
+ $target = $context->scanUntilCharacters(" \n\r\t");
+ $data = $context->scanUntilString('?>');
+ if ($data != '') {
+ $context->handler_object_pi->
+ {$context->handler_method_pi}($context->htmlsax, $target, $data);
+ }
+ $context->IgnoreCharacter();
+ $context->IgnoreCharacter();
+ return TSax3_StateParser::TSAX3_STATE_START;
+ }
+}
?>
\ No newline at end of file
diff --git a/framework/3rdParty/SafeHtml/TSafeHtmlParser.php b/framework/3rdParty/SafeHtml/TSafeHtmlParser.php
index b80f31a6..ad14baf9 100644
--- a/framework/3rdParty/SafeHtml/TSafeHtmlParser.php
+++ b/framework/3rdParty/SafeHtml/TSafeHtmlParser.php
@@ -1,673 +1,673 @@
-
- * @copyright 2004-2005 Roman Ivanov
- * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
- * @version 1.3.7
- * @link http://pixel-apes.com/safehtml/
- */
-
-
-/**
- * This package requires HTMLSax3 package
- */
-Prado::using('System.3rdParty.SafeHtml.HTMLSax3');
-
-
-/**
- *
- * TSafeHtmlParser
- *
- * This parser strips down all potentially dangerous content within HTML:
- *
- * - opening tag without its closing tag
- * - closing tag without its opening tag
- * - any of these tags: "base", "basefont", "head", "html", "body", "applet",
- * "object", "iframe", "frame", "frameset", "script", "layer", "ilayer", "embed",
- * "bgsound", "link", "meta", "style", "title", "blink", "xml" etc.
- * - any of these attributes: on*, data*, dynsrc
- * - javascript:/vbscript:/about: etc. protocols
- * - expression/behavior etc. in styles
- * - any other active content
- *
- * It also tries to convert code to XHTML valid, but htmltidy is far better
- * solution for this task.
- *
- * Example:
- *
- * $parser = Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser');
- * $result = $parser->parse($doc);
- *
- *
- * @category HTML
- * @package System.Security
- * @author Roman Ivanov
- * @copyright 1997-2005 Roman Ivanov
- * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
- * @version Release: @package_version@
- * @link http://pear.php.net/package/SafeHTML
- */
-class TSafeHtmlParser
-{
- /**
- * Storage for resulting HTML output
- *
- * @var string
- * @access private
- */
- private $_xhtml = '';
-
- /**
- * Array of counters for each tag
- *
- * @var array
- * @access private
- */
- private $_counter = array();
-
- /**
- * Stack of unclosed tags
- *
- * @var array
- * @access private
- */
- private $_stack = array();
-
- /**
- * Array of counters for tags that must be deleted with all content
- *
- * @var array
- * @access private
- */
- private $_dcCounter = array();
-
- /**
- * Stack of unclosed tags that must be deleted with all content
- *
- * @var array
- * @access private
- */
- private $_dcStack = array();
-
- /**
- * Stores level of list (ol/ul) nesting
- *
- * @var int
- * @access private
- */
- private $_listScope = 0;
-
- /**
- * Stack of unclosed list tags
- *
- * @var array
- * @access private
- */
- private $_liStack = array();
-
- /**
- * Array of prepared regular expressions for protocols (schemas) matching
- *
- * @var array
- * @access private
- */
- private $_protoRegexps = array();
-
- /**
- * Array of prepared regular expressions for CSS matching
- *
- * @var array
- * @access private
- */
- private $_cssRegexps = array();
-
- /**
- * List of single tags ("")
- *
- * @var array
- * @access public
- */
- public $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
-
- /**
- * List of dangerous tags (such tags will be deleted)
- *
- * @var array
- * @access public
- */
- public $deleteTags = array(
- 'applet', 'base', 'basefont', 'bgsound', 'blink', 'body',
- 'embed', 'frame', 'frameset', 'head', 'html', 'ilayer',
- 'iframe', 'layer', 'link', 'meta', 'object', 'style',
- 'title', 'script',
- );
-
- /**
- * List of dangerous tags (such tags will be deleted, and all content
- * inside this tags will be also removed)
- *
- * @var array
- * @access public
- */
- public $deleteTagsContent = array('script', 'style', 'title', 'xml', );
-
- /**
- * Type of protocols filtering ('white' or 'black')
- *
- * @var string
- * @access public
- */
- public $protocolFiltering = 'white';
-
- /**
- * List of "dangerous" protocols (used for blacklist-filtering)
- *
- * @var array
- * @access public
- */
- public $blackProtocols = array(
- 'about', 'chrome', 'data', 'disk', 'hcp',
- 'help', 'javascript', 'livescript', 'lynxcgi', 'lynxexec',
- 'ms-help', 'ms-its', 'mhtml', 'mocha', 'opera',
- 'res', 'resource', 'shell', 'vbscript', 'view-source',
- 'vnd.ms.radio', 'wysiwyg',
- );
-
- /**
- * List of "safe" protocols (used for whitelist-filtering)
- *
- * @var array
- * @access public
- */
- public $whiteProtocols = array(
- 'ed2k', 'file', 'ftp', 'gopher', 'http', 'https',
- 'irc', 'mailto', 'news', 'nntp', 'telnet', 'webcal',
- 'xmpp', 'callto',
- );
-
- /**
- * List of attributes that can contain protocols
- *
- * @var array
- * @access public
- */
- public $protocolAttributes = array(
- 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src',
- );
-
- /**
- * List of dangerous CSS keywords
- *
- * Whole style="" attribute will be removed, if parser will find one of
- * these keywords
- *
- * @var array
- * @access public
- */
- public $cssKeywords = array(
- 'absolute', 'behavior', 'behaviour', 'content', 'expression',
- 'fixed', 'include-source', 'moz-binding',
- );
-
- /**
- * List of tags that can have no "closing tag"
- *
- * @var array
- * @access public
- * @deprecated XHTML does not allow such tags
- */
- public $noClose = array();
-
- /**
- * List of block-level tags that terminates paragraph
- *
- * Paragraph will be closed when this tags opened
- *
- * @var array
- * @access public
- */
- public $closeParagraph = array(
- 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
- 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
- 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
- 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
- 'table', 'ul', 'xmp',
- );
-
- /**
- * List of table tags, all table tags outside a table will be removed
- *
- * @var array
- * @access public
- */
- public $tableTags = array(
- 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr',
- );
-
- /**
- * List of list tags
- *
- * @var array
- * @access public
- */
- public $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
-
- /**
- * List of dangerous attributes
- *
- * @var array
- * @access public
- */
- public $attributes = array('dynsrc');
- //public $attributes = array('dynsrc', 'id', 'name', ); //id and name are dangerous?
-
- /**
- * List of allowed "namespaced" attributes
- *
- * @var array
- * @access public
- */
- public $attributesNS = array('xml:lang', );
-
- /**
- * Constructs class
- *
- * @access public
- */
- public function __construct()
- {
- //making regular expressions based on Proto & CSS arrays
- foreach ($this->blackProtocols as $proto) {
- $preg = "/[\s\x01-\x1F]*";
- for ($i=0; $i_protoRegexps[] = $preg;
- }
-
- foreach ($this->cssKeywords as $css) {
- $this->_cssRegexps[] = '/' . $css . '/i';
- }
- return true;
- }
-
- /**
- * Handles the writing of attributes - called from $this->_openHandler()
- *
- * @param array $attrs array of attributes $name => $value
- * @return boolean
- * @access private
- */
- private function _writeAttrs ($attrs)
- {
- if (is_array($attrs)) {
- foreach ($attrs as $name => $value) {
-
- $name = strtolower($name);
-
- if (strpos($name, 'on') === 0) {
- continue;
- }
- if (strpos($name, 'data') === 0) {
- continue;
- }
- if (in_array($name, $this->attributes)) {
- continue;
- }
- if (!preg_match("/^[a-z0-9]+$/i", $name)) {
- if (!in_array($name, $this->attributesNS))
- {
- continue;
- }
- }
-
- if (($value === TRUE) || (is_null($value))) {
- $value = $name;
- }
-
- if ($name == 'style') {
-
- // removes insignificant backslahes
- $value = str_replace("\\", '', $value);
-
- // removes CSS comments
- while (1)
- {
- $_value = preg_replace("!/\*.*?\*/!s", '', $value);
- if ($_value == $value) break;
- $value = $_value;
- }
-
- // replace all & to &
- $value = str_replace('&', '&', $value);
- $value = str_replace('&', '&', $value);
-
- foreach ($this->_cssRegexps as $css) {
- if (preg_match($css, $value)) {
- continue 2;
- }
- }
- foreach ($this->_protoRegexps as $proto) {
- if (preg_match($proto, $value)) {
- continue 2;
- }
- }
- }
-
- $tempval = preg_replace('/(\d+);?/me', "chr('\\1')", $value); //"'
- $tempval = preg_replace('/([0-9a-f]+);?/mei', "chr(hexdec('\\1'))", $tempval);
-
- if ((in_array($name, $this->protocolAttributes)) &&
- (strpos($tempval, ':') !== false))
- {
- if ($this->protocolFiltering == 'black') {
- foreach ($this->_protoRegexps as $proto) {
- if (preg_match($proto, $tempval)) continue 2;
- }
- } else {
- $_tempval = explode(':', $tempval);
- $proto = $_tempval[0];
- if (!in_array($proto, $this->whiteProtocols)) {
- continue;
- }
- }
- }
-
- $value = str_replace("\"", """, $value);
- $this->_xhtml .= ' ' . $name . '="' . $value . '"';
- }
- }
- return true;
- }
-
- /**
- * Opening tag handler - called from HTMLSax
- *
- * @param object $parser HTML Parser
- * @param string $name tag name
- * @param array $attrs tag attributes
- * @return boolean
- * @access private
- */
- public function _openHandler(&$parser, $name, $attrs)
- {
- $name = strtolower($name);
-
- if (in_array($name, $this->deleteTagsContent)) {
- array_push($this->_dcStack, $name);
- $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
- }
- if (count($this->_dcStack) != 0) {
- return true;
- }
-
- if (in_array($name, $this->deleteTags)) {
- return true;
- }
-
- if (!preg_match("/^[a-z0-9]+$/i", $name)) {
- if (preg_match("!(?:\@|://)!i", $name)) {
- $this->_xhtml .= '<' . $name . '>';
- }
- return true;
- }
-
- if (in_array($name, $this->singleTags)) {
- $this->_xhtml .= '<' . $name;
- $this->_writeAttrs($attrs);
- $this->_xhtml .= ' />';
- return true;
- }
-
- // TABLES: cannot open table elements when we are not inside table
- if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0)
- && (in_array($name, $this->tableTags)))
- {
- return true;
- }
-
- // PARAGRAPHS: close paragraph when closeParagraph tags opening
- if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
- $this->_closeHandler($parser, 'p');
- }
-
- // LISTS: we should close if of the same level opening
- if ($name == 'li' && count($this->_liStack) &&
- $this->_listScope == $this->_liStack[count($this->_liStack)-1])
- {
- $this->_closeHandler($parser, 'li');
- }
-
- // LISTS: we want to know on what nesting level of lists we are
- if (in_array($name, $this->listTags)) {
- $this->_listScope++;
- }
- if ($name == 'li') {
- array_push($this->_liStack, $this->_listScope);
- }
-
- $this->_xhtml .= '<' . $name;
- $this->_writeAttrs($attrs);
- $this->_xhtml .= '>';
- array_push($this->_stack,$name);
- $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
- return true;
- }
-
- /**
- * Closing tag handler - called from HTMLSax
- *
- * @param object $parsers HTML parser
- * @param string $name tag name
- * @return boolean
- * @access private
- */
- public function _closeHandler(&$parser, $name)
- {
-
- $name = strtolower($name);
-
- if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) &&
- (in_array($name, $this->deleteTagsContent)))
- {
- while ($name != ($tag = array_pop($this->_dcStack))) {
- $this->_dcCounter[$tag]--;
- }
-
- $this->_dcCounter[$name]--;
- }
-
- if (count($this->_dcStack) != 0) {
- return true;
- }
-
- if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
- while ($name != ($tag = array_pop($this->_stack))) {
- $this->_closeTag($tag);
- }
-
- $this->_closeTag($name);
- }
- return true;
- }
-
- /**
- * Closes tag
- *
- * @param string $tag tag name
- * @return boolean
- * @access private
- */
- public function _closeTag($tag)
- {
- if (!in_array($tag, $this->noClose)) {
- $this->_xhtml .= '' . $tag . '>';
- }
-
- $this->_counter[$tag]--;
-
- if (in_array($tag, $this->listTags)) {
- $this->_listScope--;
- }
-
- if ($tag == 'li') {
- array_pop($this->_liStack);
- }
- return true;
- }
-
- /**
- * Character data handler - called from HTMLSax
- *
- * @param object $parser HTML parser
- * @param string $data textual data
- * @return boolean
- * @access private
- */
- public function _dataHandler(&$parser, $data)
- {
- if (count($this->_dcStack) == 0) {
- $this->_xhtml .= $data;
- }
- return true;
- }
-
- /**
- * Escape handler - called from HTMLSax
- *
- * @param object $parser HTML parser
- * @param string $data comments or other type of data
- * @return boolean
- * @access private
- */
- public function _escapeHandler(&$parser, $data)
- {
- return true;
- }
-
- /**
- * Returns the XHTML document
- *
- * @return string Processed (X)HTML document
- * @access public
- */
- public function getXHTML ()
- {
- while ($tag = array_pop($this->_stack)) {
- $this->_closeTag($tag);
- }
-
- return $this->_xhtml;
- }
-
- /**
- * Clears current document data
- *
- * @return boolean
- * @access public
- */
- public function clear()
- {
- $this->_xhtml = '';
- return true;
- }
-
- /**
- * Main parsing fuction
- *
- * @param string $doc HTML document for processing
- * @return string Processed (X)HTML document
- * @access public
- */
- public function parse($doc, $isUTF7=false)
- {
- $this->clear();
-
- // Save all '<' symbols
- $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '<', (string)$doc);
-
- // Web documents shouldn't contains \x00 symbol
- $doc = str_replace("\x00", '', $doc);
-
- // Opera6 bug workaround
- $doc = str_replace("\xC0\xBC", '<', $doc);
-
- // UTF-7 encoding ASCII decode
- if($isUTF7)
- $doc = $this->repackUTF7($doc);
-
- // Instantiate the parser
- $parser= new TSax3();
-
- // Set up the parser
- $parser->set_object($this);
-
- $parser->set_element_handler('_openHandler','_closeHandler');
- $parser->set_data_handler('_dataHandler');
- $parser->set_escape_handler('_escapeHandler');
-
- $parser->parse($doc);
-
- return $this->getXHTML();
-
- }
-
-
- /**
- * UTF-7 decoding fuction
- *
- * @param string $str HTML document for recode ASCII part of UTF-7 back to ASCII
- * @return string Decoded document
- * @access private
- */
- private function repackUTF7($str)
- {
- return preg_replace_callback('!\+([0-9a-zA-Z/]+)\-!', array($this, 'repackUTF7Callback'), $str);
- }
-
- /**
- * Additional UTF-7 decoding fuction
- *
- * @param string $str String for recode ASCII part of UTF-7 back to ASCII
- * @return string Recoded string
- * @access private
- */
- private function repackUTF7Callback($str)
- {
- $str = base64_decode($str[1]);
- $str = preg_replace_callback('/^((?:\x00.)*)((?:[^\x00].)+)/', array($this, 'repackUTF7Back'), $str);
- return preg_replace('/\x00(.)/', '$1', $str);
- }
-
- /**
- * Additional UTF-7 encoding fuction
- *
- * @param string $str String for recode ASCII part of UTF-7 back to ASCII
- * @return string Recoded string
- * @access private
- */
- private function repackUTF7Back($str)
- {
- return $str[1].'+'.rtrim(base64_encode($str[2]), '=').'-';
- }
-}
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * c-hanging-comment-ender-p: nil
- * End:
- */
-
+
+ * @copyright 2004-2005 Roman Ivanov
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version 1.3.7
+ * @link http://pixel-apes.com/safehtml/
+ */
+
+
+/**
+ * This package requires HTMLSax3 package
+ */
+Prado::using('System.3rdParty.SafeHtml.HTMLSax3');
+
+
+/**
+ *
+ * TSafeHtmlParser
+ *
+ * This parser strips down all potentially dangerous content within HTML:
+ *
+ * - opening tag without its closing tag
+ * - closing tag without its opening tag
+ * - any of these tags: "base", "basefont", "head", "html", "body", "applet",
+ * "object", "iframe", "frame", "frameset", "script", "layer", "ilayer", "embed",
+ * "bgsound", "link", "meta", "style", "title", "blink", "xml" etc.
+ * - any of these attributes: on*, data*, dynsrc
+ * - javascript:/vbscript:/about: etc. protocols
+ * - expression/behavior etc. in styles
+ * - any other active content
+ *
+ * It also tries to convert code to XHTML valid, but htmltidy is far better
+ * solution for this task.
+ *
+ * Example:
+ *
+ * $parser = Prado::createComponent('System.3rdParty.SafeHtml.TSafeHtmlParser');
+ * $result = $parser->parse($doc);
+ *
+ *
+ * @category HTML
+ * @package System.Security
+ * @author Roman Ivanov
+ * @copyright 1997-2005 Roman Ivanov
+ * @license http://www.debian.org/misc/bsd.license BSD License (3 Clause)
+ * @version Release: @package_version@
+ * @link http://pear.php.net/package/SafeHTML
+ */
+class TSafeHtmlParser
+{
+ /**
+ * Storage for resulting HTML output
+ *
+ * @var string
+ * @access private
+ */
+ private $_xhtml = '';
+
+ /**
+ * Array of counters for each tag
+ *
+ * @var array
+ * @access private
+ */
+ private $_counter = array();
+
+ /**
+ * Stack of unclosed tags
+ *
+ * @var array
+ * @access private
+ */
+ private $_stack = array();
+
+ /**
+ * Array of counters for tags that must be deleted with all content
+ *
+ * @var array
+ * @access private
+ */
+ private $_dcCounter = array();
+
+ /**
+ * Stack of unclosed tags that must be deleted with all content
+ *
+ * @var array
+ * @access private
+ */
+ private $_dcStack = array();
+
+ /**
+ * Stores level of list (ol/ul) nesting
+ *
+ * @var int
+ * @access private
+ */
+ private $_listScope = 0;
+
+ /**
+ * Stack of unclosed list tags
+ *
+ * @var array
+ * @access private
+ */
+ private $_liStack = array();
+
+ /**
+ * Array of prepared regular expressions for protocols (schemas) matching
+ *
+ * @var array
+ * @access private
+ */
+ private $_protoRegexps = array();
+
+ /**
+ * Array of prepared regular expressions for CSS matching
+ *
+ * @var array
+ * @access private
+ */
+ private $_cssRegexps = array();
+
+ /**
+ * List of single tags ("")
+ *
+ * @var array
+ * @access public
+ */
+ public $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
+
+ /**
+ * List of dangerous tags (such tags will be deleted)
+ *
+ * @var array
+ * @access public
+ */
+ public $deleteTags = array(
+ 'applet', 'base', 'basefont', 'bgsound', 'blink', 'body',
+ 'embed', 'frame', 'frameset', 'head', 'html', 'ilayer',
+ 'iframe', 'layer', 'link', 'meta', 'object', 'style',
+ 'title', 'script',
+ );
+
+ /**
+ * List of dangerous tags (such tags will be deleted, and all content
+ * inside this tags will be also removed)
+ *
+ * @var array
+ * @access public
+ */
+ public $deleteTagsContent = array('script', 'style', 'title', 'xml', );
+
+ /**
+ * Type of protocols filtering ('white' or 'black')
+ *
+ * @var string
+ * @access public
+ */
+ public $protocolFiltering = 'white';
+
+ /**
+ * List of "dangerous" protocols (used for blacklist-filtering)
+ *
+ * @var array
+ * @access public
+ */
+ public $blackProtocols = array(
+ 'about', 'chrome', 'data', 'disk', 'hcp',
+ 'help', 'javascript', 'livescript', 'lynxcgi', 'lynxexec',
+ 'ms-help', 'ms-its', 'mhtml', 'mocha', 'opera',
+ 'res', 'resource', 'shell', 'vbscript', 'view-source',
+ 'vnd.ms.radio', 'wysiwyg',
+ );
+
+ /**
+ * List of "safe" protocols (used for whitelist-filtering)
+ *
+ * @var array
+ * @access public
+ */
+ public $whiteProtocols = array(
+ 'ed2k', 'file', 'ftp', 'gopher', 'http', 'https',
+ 'irc', 'mailto', 'news', 'nntp', 'telnet', 'webcal',
+ 'xmpp', 'callto',
+ );
+
+ /**
+ * List of attributes that can contain protocols
+ *
+ * @var array
+ * @access public
+ */
+ public $protocolAttributes = array(
+ 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src',
+ );
+
+ /**
+ * List of dangerous CSS keywords
+ *
+ * Whole style="" attribute will be removed, if parser will find one of
+ * these keywords
+ *
+ * @var array
+ * @access public
+ */
+ public $cssKeywords = array(
+ 'absolute', 'behavior', 'behaviour', 'content', 'expression',
+ 'fixed', 'include-source', 'moz-binding',
+ );
+
+ /**
+ * List of tags that can have no "closing tag"
+ *
+ * @var array
+ * @access public
+ * @deprecated XHTML does not allow such tags
+ */
+ public $noClose = array();
+
+ /**
+ * List of block-level tags that terminates paragraph
+ *
+ * Paragraph will be closed when this tags opened
+ *
+ * @var array
+ * @access public
+ */
+ public $closeParagraph = array(
+ 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
+ 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
+ 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
+ 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
+ 'table', 'ul', 'xmp',
+ );
+
+ /**
+ * List of table tags, all table tags outside a table will be removed
+ *
+ * @var array
+ * @access public
+ */
+ public $tableTags = array(
+ 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+ 'thead', 'tr',
+ );
+
+ /**
+ * List of list tags
+ *
+ * @var array
+ * @access public
+ */
+ public $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
+
+ /**
+ * List of dangerous attributes
+ *
+ * @var array
+ * @access public
+ */
+ public $attributes = array('dynsrc');
+ //public $attributes = array('dynsrc', 'id', 'name', ); //id and name are dangerous?
+
+ /**
+ * List of allowed "namespaced" attributes
+ *
+ * @var array
+ * @access public
+ */
+ public $attributesNS = array('xml:lang', );
+
+ /**
+ * Constructs class
+ *
+ * @access public
+ */
+ public function __construct()
+ {
+ //making regular expressions based on Proto & CSS arrays
+ foreach ($this->blackProtocols as $proto) {
+ $preg = "/[\s\x01-\x1F]*";
+ for ($i=0; $i_protoRegexps[] = $preg;
+ }
+
+ foreach ($this->cssKeywords as $css) {
+ $this->_cssRegexps[] = '/' . $css . '/i';
+ }
+ return true;
+ }
+
+ /**
+ * Handles the writing of attributes - called from $this->_openHandler()
+ *
+ * @param array $attrs array of attributes $name => $value
+ * @return boolean
+ * @access private
+ */
+ private function _writeAttrs ($attrs)
+ {
+ if (is_array($attrs)) {
+ foreach ($attrs as $name => $value) {
+
+ $name = strtolower($name);
+
+ if (strpos($name, 'on') === 0) {
+ continue;
+ }
+ if (strpos($name, 'data') === 0) {
+ continue;
+ }
+ if (in_array($name, $this->attributes)) {
+ continue;
+ }
+ if (!preg_match("/^[a-z0-9]+$/i", $name)) {
+ if (!in_array($name, $this->attributesNS))
+ {
+ continue;
+ }
+ }
+
+ if (($value === TRUE) || (is_null($value))) {
+ $value = $name;
+ }
+
+ if ($name == 'style') {
+
+ // removes insignificant backslahes
+ $value = str_replace("\\", '', $value);
+
+ // removes CSS comments
+ while (1)
+ {
+ $_value = preg_replace("!/\*.*?\*/!s", '', $value);
+ if ($_value == $value) break;
+ $value = $_value;
+ }
+
+ // replace all & to &
+ $value = str_replace('&', '&', $value);
+ $value = str_replace('&', '&', $value);
+
+ foreach ($this->_cssRegexps as $css) {
+ if (preg_match($css, $value)) {
+ continue 2;
+ }
+ }
+ foreach ($this->_protoRegexps as $proto) {
+ if (preg_match($proto, $value)) {
+ continue 2;
+ }
+ }
+ }
+
+ $tempval = preg_replace('/(\d+);?/me', "chr('\\1')", $value); //"'
+ $tempval = preg_replace('/([0-9a-f]+);?/mei', "chr(hexdec('\\1'))", $tempval);
+
+ if ((in_array($name, $this->protocolAttributes)) &&
+ (strpos($tempval, ':') !== false))
+ {
+ if ($this->protocolFiltering == 'black') {
+ foreach ($this->_protoRegexps as $proto) {
+ if (preg_match($proto, $tempval)) continue 2;
+ }
+ } else {
+ $_tempval = explode(':', $tempval);
+ $proto = $_tempval[0];
+ if (!in_array($proto, $this->whiteProtocols)) {
+ continue;
+ }
+ }
+ }
+
+ $value = str_replace("\"", """, $value);
+ $this->_xhtml .= ' ' . $name . '="' . $value . '"';
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Opening tag handler - called from HTMLSax
+ *
+ * @param object $parser HTML Parser
+ * @param string $name tag name
+ * @param array $attrs tag attributes
+ * @return boolean
+ * @access private
+ */
+ public function _openHandler(&$parser, $name, $attrs)
+ {
+ $name = strtolower($name);
+
+ if (in_array($name, $this->deleteTagsContent)) {
+ array_push($this->_dcStack, $name);
+ $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
+ }
+ if (count($this->_dcStack) != 0) {
+ return true;
+ }
+
+ if (in_array($name, $this->deleteTags)) {
+ return true;
+ }
+
+ if (!preg_match("/^[a-z0-9]+$/i", $name)) {
+ if (preg_match("!(?:\@|://)!i", $name)) {
+ $this->_xhtml .= '<' . $name . '>';
+ }
+ return true;
+ }
+
+ if (in_array($name, $this->singleTags)) {
+ $this->_xhtml .= '<' . $name;
+ $this->_writeAttrs($attrs);
+ $this->_xhtml .= ' />';
+ return true;
+ }
+
+ // TABLES: cannot open table elements when we are not inside table
+ if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0)
+ && (in_array($name, $this->tableTags)))
+ {
+ return true;
+ }
+
+ // PARAGRAPHS: close paragraph when closeParagraph tags opening
+ if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
+ $this->_closeHandler($parser, 'p');
+ }
+
+ // LISTS: we should close if of the same level opening
+ if ($name == 'li' && count($this->_liStack) &&
+ $this->_listScope == $this->_liStack[count($this->_liStack)-1])
+ {
+ $this->_closeHandler($parser, 'li');
+ }
+
+ // LISTS: we want to know on what nesting level of lists we are
+ if (in_array($name, $this->listTags)) {
+ $this->_listScope++;
+ }
+ if ($name == 'li') {
+ array_push($this->_liStack, $this->_listScope);
+ }
+
+ $this->_xhtml .= '<' . $name;
+ $this->_writeAttrs($attrs);
+ $this->_xhtml .= '>';
+ array_push($this->_stack,$name);
+ $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
+ return true;
+ }
+
+ /**
+ * Closing tag handler - called from HTMLSax
+ *
+ * @param object $parsers HTML parser
+ * @param string $name tag name
+ * @return boolean
+ * @access private
+ */
+ public function _closeHandler(&$parser, $name)
+ {
+
+ $name = strtolower($name);
+
+ if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) &&
+ (in_array($name, $this->deleteTagsContent)))
+ {
+ while ($name != ($tag = array_pop($this->_dcStack))) {
+ $this->_dcCounter[$tag]--;
+ }
+
+ $this->_dcCounter[$name]--;
+ }
+
+ if (count($this->_dcStack) != 0) {
+ return true;
+ }
+
+ if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
+ while ($name != ($tag = array_pop($this->_stack))) {
+ $this->_closeTag($tag);
+ }
+
+ $this->_closeTag($name);
+ }
+ return true;
+ }
+
+ /**
+ * Closes tag
+ *
+ * @param string $tag tag name
+ * @return boolean
+ * @access private
+ */
+ public function _closeTag($tag)
+ {
+ if (!in_array($tag, $this->noClose)) {
+ $this->_xhtml .= '' . $tag . '>';
+ }
+
+ $this->_counter[$tag]--;
+
+ if (in_array($tag, $this->listTags)) {
+ $this->_listScope--;
+ }
+
+ if ($tag == 'li') {
+ array_pop($this->_liStack);
+ }
+ return true;
+ }
+
+ /**
+ * Character data handler - called from HTMLSax
+ *
+ * @param object $parser HTML parser
+ * @param string $data textual data
+ * @return boolean
+ * @access private
+ */
+ public function _dataHandler(&$parser, $data)
+ {
+ if (count($this->_dcStack) == 0) {
+ $this->_xhtml .= $data;
+ }
+ return true;
+ }
+
+ /**
+ * Escape handler - called from HTMLSax
+ *
+ * @param object $parser HTML parser
+ * @param string $data comments or other type of data
+ * @return boolean
+ * @access private
+ */
+ public function _escapeHandler(&$parser, $data)
+ {
+ return true;
+ }
+
+ /**
+ * Returns the XHTML document
+ *
+ * @return string Processed (X)HTML document
+ * @access public
+ */
+ public function getXHTML ()
+ {
+ while ($tag = array_pop($this->_stack)) {
+ $this->_closeTag($tag);
+ }
+
+ return $this->_xhtml;
+ }
+
+ /**
+ * Clears current document data
+ *
+ * @return boolean
+ * @access public
+ */
+ public function clear()
+ {
+ $this->_xhtml = '';
+ return true;
+ }
+
+ /**
+ * Main parsing fuction
+ *
+ * @param string $doc HTML document for processing
+ * @return string Processed (X)HTML document
+ * @access public
+ */
+ public function parse($doc, $isUTF7=false)
+ {
+ $this->clear();
+
+ // Save all '<' symbols
+ $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '<', (string)$doc);
+
+ // Web documents shouldn't contains \x00 symbol
+ $doc = str_replace("\x00", '', $doc);
+
+ // Opera6 bug workaround
+ $doc = str_replace("\xC0\xBC", '<', $doc);
+
+ // UTF-7 encoding ASCII decode
+ if($isUTF7)
+ $doc = $this->repackUTF7($doc);
+
+ // Instantiate the parser
+ $parser= new TSax3();
+
+ // Set up the parser
+ $parser->set_object($this);
+
+ $parser->set_element_handler('_openHandler','_closeHandler');
+ $parser->set_data_handler('_dataHandler');
+ $parser->set_escape_handler('_escapeHandler');
+
+ $parser->parse($doc);
+
+ return $this->getXHTML();
+
+ }
+
+
+ /**
+ * UTF-7 decoding fuction
+ *
+ * @param string $str HTML document for recode ASCII part of UTF-7 back to ASCII
+ * @return string Decoded document
+ * @access private
+ */
+ private function repackUTF7($str)
+ {
+ return preg_replace_callback('!\+([0-9a-zA-Z/]+)\-!', array($this, 'repackUTF7Callback'), $str);
+ }
+
+ /**
+ * Additional UTF-7 decoding fuction
+ *
+ * @param string $str String for recode ASCII part of UTF-7 back to ASCII
+ * @return string Recoded string
+ * @access private
+ */
+ private function repackUTF7Callback($str)
+ {
+ $str = base64_decode($str[1]);
+ $str = preg_replace_callback('/^((?:\x00.)*)((?:[^\x00].)+)/', array($this, 'repackUTF7Back'), $str);
+ return preg_replace('/\x00(.)/', '$1', $str);
+ }
+
+ /**
+ * Additional UTF-7 encoding fuction
+ *
+ * @param string $str String for recode ASCII part of UTF-7 back to ASCII
+ * @return string Recoded string
+ * @access private
+ */
+ private function repackUTF7Back($str)
+ {
+ return $str[1].'+'.rtrim(base64_encode($str[2]), '=').'-';
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
?>
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter.php b/framework/3rdParty/TextHighlighter/Text/Highlighter.php
index b597b909..9ec09cdb 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter.php
@@ -1,397 +1,397 @@
-
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version CVS: $Id: Highlighter.php,v 1.1 2007/06/03 02:35:28 ssttoo Exp $
- * @link http://pear.php.net/package/Text_Highlighter
- */
-
-// {{{ BC constants
-
-// BC trick : define constants related to default
-// renderer if needed
-if (!defined('HL_NUMBERS_LI')) {
- /**#@+
- * Constant for use with $options['numbers']
- * @see Text_Highlighter_Renderer_Html::_init()
- */
- /**
- * use numbered list
- */
- define ('HL_NUMBERS_LI' , 1);
- /**
- * Use 2-column table with line numbers in left column and code in right column.
- * Forces $options['tag'] = HL_TAG_PRE
- */
- define ('HL_NUMBERS_TABLE' , 2);
- /**#@-*/
-}
-
-// }}}
-// {{{ constants
-/**
- * for our purpose, it is infinity
- */
-define ('HL_INFINITY', 1000000000);
-
-// }}}
-
-/**
- * Text highlighter base class
- *
- * @author Andrey Demenev
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-
-// {{{ Text_Highlighter
-
-/**
- * Text highlighter base class
- *
- * This class implements all functions necessary for highlighting,
- * but it does not contain highlighting rules. Actual highlighting is
- * done using a descendent of this class.
- *
- * One is not supposed to manually create descendent classes.
- * Instead, describe highlighting rules in XML format and
- * use {@link Text_Highlighter_Generator} to create descendent class.
- * Alternatively, an instance of a descendent class can be created
- * directly.
- *
- * Use {@link Text_Highlighter::factory()} to create an
- * object for particular language highlighter
- *
- * Usage example
- *
- *require_once 'Text/Highlighter.php';
- *$hlSQL =& Text_Highlighter::factory('SQL',array('numbers'=>true));
- *echo $hlSQL->highlight('SELECT * FROM table a WHERE id = 12');
- *
- *
- * @author Andrey Demenev
- * @package Text_Highlighter
- * @access public
- */
-
-class Text_Highlighter
-{
- // {{{ members
-
- /**
- * Syntax highlighting rules.
- * Auto-generated classes set this var
- *
- * @access protected
- * @see _init
- * @var array
- */
- var $_syntax;
-
- /**
- * Renderer object.
- *
- * @access private
- * @var array
- */
- var $_renderer;
-
- /**
- * Options. Keeped for BC
- *
- * @access protected
- * @var array
- */
- var $_options = array();
-
- /**
- * Conditionds
- *
- * @access protected
- * @var array
- */
- var $_conditions = array();
-
- /**
- * Disabled keywords
- *
- * @access protected
- * @var array
- */
- var $_disabled = array();
-
- /**
- * Language
- *
- * @access protected
- * @var string
- */
- var $_language = '';
-
- // }}}
- // {{{ _checkDefines
-
- /**
- * Called by subclssses' constructors to enable/disable
- * optional highlighter rules
- *
- * @param array $defines Conditional defines
- *
- * @access protected
- */
- function _checkDefines()
- {
- if (isset($this->_options['defines'])) {
- $defines = $this->_options['defines'];
- } else {
- $defines = array();
- }
- foreach ($this->_conditions as $name => $actions) {
- foreach($actions as $action) {
- $present = in_array($name, $defines);
- if (!$action[1]) {
- $present = !$present;
- }
- if ($present) {
- unset($this->_disabled[$action[0]]);
- } else {
- $this->_disabled[$action[0]] = true;
- }
- }
- }
- }
-
- // }}}
- // {{{ factory
-
- /**
- * Create a new Highlighter object for specified language
- *
- * @param string $lang language, for example "SQL"
- * @param array $options Rendering options. This
- * parameter is only keeped for BC reasons, use
- * {@link Text_Highlighter::setRenderer()} instead
- *
- * @return mixed a newly created Highlighter object, or
- * a PEAR error object on error
- *
- * @static
- * @access public
- */
- public static function factory($lang, $options = array())
- {
- $lang = strtoupper($lang);
- $langFile = dirname(__FILE__)."/Highlighter/$lang.php";
- if (is_file($langFile))
- include_once $langFile;
- else
- return false;
-
- $classname = 'Text_Highlighter_' . $lang;
-
- if (!class_exists($classname))
- return false;
-
- return new $classname($options);
- }
-
- // }}}
- // {{{ setRenderer
-
- /**
- * Set renderer object
- *
- * @param object $renderer Text_Highlighter_Renderer
- *
- * @access public
- */
- function setRenderer($renderer)
- {
- $this->_renderer = $renderer;
- }
-
- // }}}
-
- /**
- * Helper function to find matching brackets
- *
- * @access private
- */
- function _matchingBrackets($str)
- {
- return strtr($str, '()<>[]{}', ')(><][}{');
- }
-
-
-
-
- function _getToken()
- {
- if (!empty($this->_tokenStack)) {
- return array_pop($this->_tokenStack);
- }
- if ($this->_pos >= $this->_len) {
- return NULL;
- }
-
- if ($this->_state != -1 && preg_match($this->_endpattern, $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos)) {
- $endpos = $m[0][1];
- $endmatch = $m[0][0];
- } else {
- $endpos = -1;
- }
- preg_match ($this->_regs[$this->_state], $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos);
- $n = 1;
-
-
- foreach ($this->_counts[$this->_state] as $i=>$count) {
- if (!isset($m[$n])) {
- break;
- }
- if ($m[$n][1]>-1 && ($endpos == -1 || $m[$n][1] < $endpos)) {
- if ($this->_states[$this->_state][$i] != -1) {
- $this->_tokenStack[] = array($this->_delim[$this->_state][$i], $m[$n][0]);
- } else {
- $inner = $this->_inner[$this->_state][$i];
- if (isset($this->_parts[$this->_state][$i])) {
- $parts = array();
- $partpos = $m[$n][1];
- for ($j=1; $j<=$count; $j++) {
- if ($m[$j+$n][1] < 0) {
- continue;
- }
- if (isset($this->_parts[$this->_state][$i][$j])) {
- if ($m[$j+$n][1] > $partpos) {
- array_unshift($parts, array($inner, substr($this->_str, $partpos, $m[$j+$n][1]-$partpos)));
- }
- array_unshift($parts, array($this->_parts[$this->_state][$i][$j], $m[$j+$n][0]));
- }
- $partpos = $m[$j+$n][1] + strlen($m[$j+$n][0]);
- }
- if ($partpos < $m[$n][1] + strlen($m[$n][0])) {
- array_unshift($parts, array($inner, substr($this->_str, $partpos, $m[$n][1] - $partpos + strlen($m[$n][0]))));
- }
- $this->_tokenStack = array_merge($this->_tokenStack, $parts);
- } else {
- foreach ($this->_keywords[$this->_state][$i] as $g => $re) {
- if (isset($this->_disabled[$g])) {
- continue;
- }
- if (preg_match($re, $m[$n][0])) {
- $inner = $this->_kwmap[$g];
- break;
- }
- }
- $this->_tokenStack[] = array($inner, $m[$n][0]);
- }
- }
- if ($m[$n][1] > $this->_pos) {
- $this->_tokenStack[] = array($this->_lastinner, substr($this->_str, $this->_pos, $m[$n][1]-$this->_pos));
- }
- $this->_pos = $m[$n][1] + strlen($m[$n][0]);
- if ($this->_states[$this->_state][$i] != -1) {
- $this->_stack[] = array($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern);
- $this->_lastinner = $this->_inner[$this->_state][$i];
- $this->_lastdelim = $this->_delim[$this->_state][$i];
- $l = $this->_state;
- $this->_state = $this->_states[$this->_state][$i];
- $this->_endpattern = $this->_end[$this->_state];
- if ($this->_subst[$l][$i]) {
- for ($k=0; $k<=$this->_counts[$l][$i]; $k++) {
- if (!isset($m[$i+$k])) {
- break;
- }
- $quoted = preg_quote($m[$n+$k][0], '/');
- $this->_endpattern = str_replace('%'.$k.'%', $quoted, $this->_endpattern);
- $this->_endpattern = str_replace('%b'.$k.'%', $this->_matchingBrackets($quoted), $this->_endpattern);
- }
- }
- }
- return array_pop($this->_tokenStack);
- }
- $n += $count + 1;
- }
-
- if ($endpos > -1) {
- $this->_tokenStack[] = array($this->_lastdelim, $endmatch);
- if ($endpos > $this->_pos) {
- $this->_tokenStack[] = array($this->_lastinner, substr($this->_str, $this->_pos, $endpos-$this->_pos));
- }
- list($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern) = array_pop($this->_stack);
- $this->_pos = $endpos + strlen($endmatch);
- return array_pop($this->_tokenStack);
- }
- $p = $this->_pos;
- $this->_pos = HL_INFINITY;
- return array($this->_lastinner, substr($this->_str, $p));
- }
-
-
-
-
- // {{{ highlight
-
- /**
- * Highlights code
- *
- * @param string $str Code to highlight
- * @access public
- * @return string Highlighted text
- *
- */
-
- function highlight($str)
- {
- if (!($this->_renderer)) {
- include_once('Text/Highlighter/Renderer/Html.php');
- $this->_renderer = new Text_Highlighter_Renderer_Html($this->_options);
- }
- $this->_state = -1;
- $this->_pos = 0;
- $this->_stack = array();
- $this->_tokenStack = array();
- $this->_lastinner = $this->_defClass;
- $this->_lastdelim = $this->_defClass;
- $this->_endpattern = '';
- $this->_renderer->reset();
- $this->_renderer->setCurrentLanguage($this->_language);
- $this->_str = $this->_renderer->preprocess($str);
- $this->_len = strlen($this->_str);
- while ($token = $this->_getToken()) {
- $this->_renderer->acceptToken($token[0], $token[1]);
- }
- $this->_renderer->finalize();
- return $this->_renderer->getOutput();
- }
-
- // }}}
-
-}
-
-// }}}
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * c-hanging-comment-ender-p: nil
- * End:
- */
-
+
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version CVS: $Id: Highlighter.php,v 1.1 2007/06/03 02:35:28 ssttoo Exp $
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+
+// {{{ BC constants
+
+// BC trick : define constants related to default
+// renderer if needed
+if (!defined('HL_NUMBERS_LI')) {
+ /**#@+
+ * Constant for use with $options['numbers']
+ * @see Text_Highlighter_Renderer_Html::_init()
+ */
+ /**
+ * use numbered list
+ */
+ define ('HL_NUMBERS_LI' , 1);
+ /**
+ * Use 2-column table with line numbers in left column and code in right column.
+ * Forces $options['tag'] = HL_TAG_PRE
+ */
+ define ('HL_NUMBERS_TABLE' , 2);
+ /**#@-*/
+}
+
+// }}}
+// {{{ constants
+/**
+ * for our purpose, it is infinity
+ */
+define ('HL_INFINITY', 1000000000);
+
+// }}}
+
+/**
+ * Text highlighter base class
+ *
+ * @author Andrey Demenev
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+
+// {{{ Text_Highlighter
+
+/**
+ * Text highlighter base class
+ *
+ * This class implements all functions necessary for highlighting,
+ * but it does not contain highlighting rules. Actual highlighting is
+ * done using a descendent of this class.
+ *
+ * One is not supposed to manually create descendent classes.
+ * Instead, describe highlighting rules in XML format and
+ * use {@link Text_Highlighter_Generator} to create descendent class.
+ * Alternatively, an instance of a descendent class can be created
+ * directly.
+ *
+ * Use {@link Text_Highlighter::factory()} to create an
+ * object for particular language highlighter
+ *
+ * Usage example
+ *
+ *require_once 'Text/Highlighter.php';
+ *$hlSQL =& Text_Highlighter::factory('SQL',array('numbers'=>true));
+ *echo $hlSQL->highlight('SELECT * FROM table a WHERE id = 12');
+ *
+ *
+ * @author Andrey Demenev
+ * @package Text_Highlighter
+ * @access public
+ */
+
+class Text_Highlighter
+{
+ // {{{ members
+
+ /**
+ * Syntax highlighting rules.
+ * Auto-generated classes set this var
+ *
+ * @access protected
+ * @see _init
+ * @var array
+ */
+ var $_syntax;
+
+ /**
+ * Renderer object.
+ *
+ * @access private
+ * @var array
+ */
+ var $_renderer;
+
+ /**
+ * Options. Keeped for BC
+ *
+ * @access protected
+ * @var array
+ */
+ var $_options = array();
+
+ /**
+ * Conditionds
+ *
+ * @access protected
+ * @var array
+ */
+ var $_conditions = array();
+
+ /**
+ * Disabled keywords
+ *
+ * @access protected
+ * @var array
+ */
+ var $_disabled = array();
+
+ /**
+ * Language
+ *
+ * @access protected
+ * @var string
+ */
+ var $_language = '';
+
+ // }}}
+ // {{{ _checkDefines
+
+ /**
+ * Called by subclssses' constructors to enable/disable
+ * optional highlighter rules
+ *
+ * @param array $defines Conditional defines
+ *
+ * @access protected
+ */
+ function _checkDefines()
+ {
+ if (isset($this->_options['defines'])) {
+ $defines = $this->_options['defines'];
+ } else {
+ $defines = array();
+ }
+ foreach ($this->_conditions as $name => $actions) {
+ foreach($actions as $action) {
+ $present = in_array($name, $defines);
+ if (!$action[1]) {
+ $present = !$present;
+ }
+ if ($present) {
+ unset($this->_disabled[$action[0]]);
+ } else {
+ $this->_disabled[$action[0]] = true;
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ factory
+
+ /**
+ * Create a new Highlighter object for specified language
+ *
+ * @param string $lang language, for example "SQL"
+ * @param array $options Rendering options. This
+ * parameter is only keeped for BC reasons, use
+ * {@link Text_Highlighter::setRenderer()} instead
+ *
+ * @return mixed a newly created Highlighter object, or
+ * a PEAR error object on error
+ *
+ * @static
+ * @access public
+ */
+ public static function factory($lang, $options = array())
+ {
+ $lang = strtoupper($lang);
+ $langFile = dirname(__FILE__)."/Highlighter/$lang.php";
+ if (is_file($langFile))
+ include_once $langFile;
+ else
+ return false;
+
+ $classname = 'Text_Highlighter_' . $lang;
+
+ if (!class_exists($classname))
+ return false;
+
+ return new $classname($options);
+ }
+
+ // }}}
+ // {{{ setRenderer
+
+ /**
+ * Set renderer object
+ *
+ * @param object $renderer Text_Highlighter_Renderer
+ *
+ * @access public
+ */
+ function setRenderer($renderer)
+ {
+ $this->_renderer = $renderer;
+ }
+
+ // }}}
+
+ /**
+ * Helper function to find matching brackets
+ *
+ * @access private
+ */
+ function _matchingBrackets($str)
+ {
+ return strtr($str, '()<>[]{}', ')(><][}{');
+ }
+
+
+
+
+ function _getToken()
+ {
+ if (!empty($this->_tokenStack)) {
+ return array_pop($this->_tokenStack);
+ }
+ if ($this->_pos >= $this->_len) {
+ return NULL;
+ }
+
+ if ($this->_state != -1 && preg_match($this->_endpattern, $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos)) {
+ $endpos = $m[0][1];
+ $endmatch = $m[0][0];
+ } else {
+ $endpos = -1;
+ }
+ preg_match ($this->_regs[$this->_state], $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos);
+ $n = 1;
+
+
+ foreach ($this->_counts[$this->_state] as $i=>$count) {
+ if (!isset($m[$n])) {
+ break;
+ }
+ if ($m[$n][1]>-1 && ($endpos == -1 || $m[$n][1] < $endpos)) {
+ if ($this->_states[$this->_state][$i] != -1) {
+ $this->_tokenStack[] = array($this->_delim[$this->_state][$i], $m[$n][0]);
+ } else {
+ $inner = $this->_inner[$this->_state][$i];
+ if (isset($this->_parts[$this->_state][$i])) {
+ $parts = array();
+ $partpos = $m[$n][1];
+ for ($j=1; $j<=$count; $j++) {
+ if ($m[$j+$n][1] < 0) {
+ continue;
+ }
+ if (isset($this->_parts[$this->_state][$i][$j])) {
+ if ($m[$j+$n][1] > $partpos) {
+ array_unshift($parts, array($inner, substr($this->_str, $partpos, $m[$j+$n][1]-$partpos)));
+ }
+ array_unshift($parts, array($this->_parts[$this->_state][$i][$j], $m[$j+$n][0]));
+ }
+ $partpos = $m[$j+$n][1] + strlen($m[$j+$n][0]);
+ }
+ if ($partpos < $m[$n][1] + strlen($m[$n][0])) {
+ array_unshift($parts, array($inner, substr($this->_str, $partpos, $m[$n][1] - $partpos + strlen($m[$n][0]))));
+ }
+ $this->_tokenStack = array_merge($this->_tokenStack, $parts);
+ } else {
+ foreach ($this->_keywords[$this->_state][$i] as $g => $re) {
+ if (isset($this->_disabled[$g])) {
+ continue;
+ }
+ if (preg_match($re, $m[$n][0])) {
+ $inner = $this->_kwmap[$g];
+ break;
+ }
+ }
+ $this->_tokenStack[] = array($inner, $m[$n][0]);
+ }
+ }
+ if ($m[$n][1] > $this->_pos) {
+ $this->_tokenStack[] = array($this->_lastinner, substr($this->_str, $this->_pos, $m[$n][1]-$this->_pos));
+ }
+ $this->_pos = $m[$n][1] + strlen($m[$n][0]);
+ if ($this->_states[$this->_state][$i] != -1) {
+ $this->_stack[] = array($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern);
+ $this->_lastinner = $this->_inner[$this->_state][$i];
+ $this->_lastdelim = $this->_delim[$this->_state][$i];
+ $l = $this->_state;
+ $this->_state = $this->_states[$this->_state][$i];
+ $this->_endpattern = $this->_end[$this->_state];
+ if ($this->_subst[$l][$i]) {
+ for ($k=0; $k<=$this->_counts[$l][$i]; $k++) {
+ if (!isset($m[$i+$k])) {
+ break;
+ }
+ $quoted = preg_quote($m[$n+$k][0], '/');
+ $this->_endpattern = str_replace('%'.$k.'%', $quoted, $this->_endpattern);
+ $this->_endpattern = str_replace('%b'.$k.'%', $this->_matchingBrackets($quoted), $this->_endpattern);
+ }
+ }
+ }
+ return array_pop($this->_tokenStack);
+ }
+ $n += $count + 1;
+ }
+
+ if ($endpos > -1) {
+ $this->_tokenStack[] = array($this->_lastdelim, $endmatch);
+ if ($endpos > $this->_pos) {
+ $this->_tokenStack[] = array($this->_lastinner, substr($this->_str, $this->_pos, $endpos-$this->_pos));
+ }
+ list($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern) = array_pop($this->_stack);
+ $this->_pos = $endpos + strlen($endmatch);
+ return array_pop($this->_tokenStack);
+ }
+ $p = $this->_pos;
+ $this->_pos = HL_INFINITY;
+ return array($this->_lastinner, substr($this->_str, $p));
+ }
+
+
+
+
+ // {{{ highlight
+
+ /**
+ * Highlights code
+ *
+ * @param string $str Code to highlight
+ * @access public
+ * @return string Highlighted text
+ *
+ */
+
+ function highlight($str)
+ {
+ if (!($this->_renderer)) {
+ include_once('Text/Highlighter/Renderer/Html.php');
+ $this->_renderer = new Text_Highlighter_Renderer_Html($this->_options);
+ }
+ $this->_state = -1;
+ $this->_pos = 0;
+ $this->_stack = array();
+ $this->_tokenStack = array();
+ $this->_lastinner = $this->_defClass;
+ $this->_lastdelim = $this->_defClass;
+ $this->_endpattern = '';
+ $this->_renderer->reset();
+ $this->_renderer->setCurrentLanguage($this->_language);
+ $this->_str = $this->_renderer->preprocess($str);
+ $this->_len = strlen($this->_str);
+ while ($token = $this->_getToken()) {
+ $this->_renderer->acceptToken($token[0], $token[1]);
+ }
+ $this->_renderer->finalize();
+ return $this->_renderer->getOutput();
+ }
+
+ // }}}
+
+}
+
+// }}}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
?>
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter/ABAP.php b/framework/3rdParty/TextHighlighter/Text/Highlighter/ABAP.php
index adeaabf9..153ce9fe 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter/ABAP.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter/ABAP.php
@@ -1,53 +1,53 @@
-
- *
- */
-
-/**
- * @ignore
- */
-
-/**
- * Auto-generated class. ABAP syntax highlighting
- *
+ *
+ */
+
+/**
+ * @ignore
+ */
+
+/**
+ * Auto-generated class. ABAP syntax highlighting
+ *
* @author Stoyan Stefanov
- * @category Text
- * @package Text_Highlighter
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-class Text_Highlighter_ABAP extends Text_Highlighter
-{
+ * @category Text
+ * @package Text_Highlighter
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+class Text_Highlighter_ABAP extends Text_Highlighter
+{
var $_language = 'abap';
- /**
- * Constructor
- *
- * @param array $options
- * @access public
- */
- function __construct($options=array())
- {
-
+ /**
+ * Constructor
+ *
+ * @param array $options
+ * @access public
+ */
+ function __construct($options=array())
+ {
+
$this->_options = $options;
$this->_regs = array (
-1 => '/((?i)\\{)|((?i)\\()|((?i)\\[)|((?i)^\\*|")|((?i)\')|((?i)[a-z_\\-]\\w*)/',
@@ -498,8 +498,8 @@ class Text_Highlighter_ABAP extends Text_Highlighter
'reserved' => 'reserved',
'constants' => 'reserved',
);
- $this->_defClass = 'code';
- $this->_checkDefines();
- }
-
+ $this->_defClass = 'code';
+ $this->_checkDefines();
+ }
+
}
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter/CPP.php b/framework/3rdParty/TextHighlighter/Text/Highlighter/CPP.php
index 7be6add9..761297ac 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter/CPP.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter/CPP.php
@@ -1,56 +1,56 @@
-
- *
- */
-
-/**
- * Auto-generated class. CPP syntax highlighting
- *
+ *
+ */
+
+/**
+ * Auto-generated class. CPP syntax highlighting
+ *
* @author Aaron Kalin
* @author Andrey Demenev
- * @category Text
- * @package Text_Highlighter
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-class Text_Highlighter_CPP extends Text_Highlighter
-{
+ * @category Text
+ * @package Text_Highlighter
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+class Text_Highlighter_CPP extends Text_Highlighter
+{
var $_language = 'cpp';
- /**
- * Constructor
- *
- * @param array $options
- * @access public
- */
- function __construct($options=array())
- {
-
+ /**
+ * Constructor
+ *
+ * @param array $options
+ * @access public
+ */
+ function __construct($options=array())
+ {
+
$this->_options = $options;
$this->_regs = array (
-1 => '/((?i)")|((?i)\\{)|((?i)\\()|((?i)\\[)|((?i)[a-z_]\\w*)|((?mi)^[ \\t]*#include)|((?mii)^[ \\t]*#[ \\t]*[a-z]+)|((?i)\\d*\\.?\\d+)|((?i)\\/\\*)|((?i)\\/\\/.+)/',
@@ -833,8 +833,8 @@ class Text_Highlighter_CPP extends Text_Highlighter
'types' => 'types',
'Common Macros' => 'prepro',
);
- $this->_defClass = 'code';
- $this->_checkDefines();
- }
-
+ $this->_defClass = 'code';
+ $this->_checkDefines();
+ }
+
}
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter/CSS.php b/framework/3rdParty/TextHighlighter/Text/Highlighter/CSS.php
index 04705ff6..28f92f34 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter/CSS.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter/CSS.php
@@ -1,49 +1,49 @@
-
- *
- */
-
-/**
- * Auto-generated class. CSS syntax highlighting
- *
+ *
+ */
+
+/**
+ * Auto-generated class. CSS syntax highlighting
+ *
* @author Andrey Demenev
- * @category Text
- * @package Text_Highlighter
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-class Text_Highlighter_CSS extends Text_Highlighter
-{
+ * @category Text
+ * @package Text_Highlighter
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+class Text_Highlighter_CSS extends Text_Highlighter
+{
var $_language = 'css';
- /**
- * Constructor
- *
- * @param array $options
- * @access public
- */
- function __construct($options=array())
- {
-
+ /**
+ * Constructor
+ *
+ * @param array $options
+ * @access public
+ */
+ function __construct($options=array())
+ {
+
$this->_options = $options;
$this->_regs = array (
-1 => '/((?i)(@[a-z\\d]+))|((?i)(((\\.|#)?[a-z]+[a-z\\d\\-]*(?![a-z\\d\\-]))|(\\*))(?!\\s*:\\s*[\\s\\{]))|((?i):[a-z][a-z\\d\\-]*)|((?i)\\[)|((?i)\\{)/',
@@ -376,8 +376,8 @@ class Text_Highlighter_CSS extends Text_Highlighter
'propertyValue' => 'string',
'namedcolor' => 'var',
);
- $this->_defClass = 'code';
- $this->_checkDefines();
- }
-
+ $this->_defClass = 'code';
+ $this->_checkDefines();
+ }
+
}
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter/DIFF.php b/framework/3rdParty/TextHighlighter/Text/Highlighter/DIFF.php
index c27301f5..21e97087 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter/DIFF.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter/DIFF.php
@@ -1,49 +1,49 @@
-
- *
- */
-
-/**
- * Auto-generated class. DIFF syntax highlighting
- *
+ *
+ */
+
+/**
+ * Auto-generated class. DIFF syntax highlighting
+ *
* @author Andrey Demenev
- * @category Text
- * @package Text_Highlighter
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-class Text_Highlighter_DIFF extends Text_Highlighter
-{
+ * @category Text
+ * @package Text_Highlighter
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+class Text_Highlighter_DIFF extends Text_Highlighter
+{
var $_language = 'diff';
- /**
- * Constructor
- *
- * @param array $options
- * @access public
- */
- function __construct($options=array())
- {
-
+ /**
+ * Constructor
+ *
+ * @param array $options
+ * @access public
+ */
+ function __construct($options=array())
+ {
+
$this->_options = $options;
$this->_regs = array (
-1 => '/((?m)^\\\\\\sNo\\snewline.+$)|((?m)^\\-\\-\\-$)|((?m)^(diff\\s+\\-|Only\\s+|Index).*$)|((?m)^(\\-\\-\\-|\\+\\+\\+)\\s.+$)|((?m)^\\*.*$)|((?m)^\\+.*$)|((?m)^!.*$)|((?m)^\\<\\s.*$)|((?m)^\\>\\s.*$)|((?m)^\\d+(\\,\\d+)?[acd]\\d+(,\\d+)?$)|((?m)^\\-.*$)|((?m)^\\+.*$)|((?m)^@@.+@@$)|((?m)^d\\d+\\s\\d+$)|((?m)^a\\d+\\s\\d+$)|((?m)^(\\d+)(,\\d+)?(a)$)|((?m)^(\\d+)(,\\d+)?(c)$)|((?m)^(\\d+)(,\\d+)?(d)$)|((?m)^a(\\d+)(\\s\\d+)?$)|((?m)^c(\\d+)(\\s\\d+)?$)|((?m)^d(\\d+)(\\s\\d+)?$)/',
@@ -359,8 +359,8 @@ class Text_Highlighter_DIFF extends Text_Highlighter
);
$this->_kwmap = array (
);
- $this->_defClass = 'default';
- $this->_checkDefines();
- }
-
+ $this->_defClass = 'default';
+ $this->_checkDefines();
+ }
+
}
\ No newline at end of file
diff --git a/framework/3rdParty/TextHighlighter/Text/Highlighter/DTD.php b/framework/3rdParty/TextHighlighter/Text/Highlighter/DTD.php
index ad33e528..1d4a403e 100644
--- a/framework/3rdParty/TextHighlighter/Text/Highlighter/DTD.php
+++ b/framework/3rdParty/TextHighlighter/Text/Highlighter/DTD.php
@@ -1,49 +1,49 @@
-
- *
- */
-
-/**
- * Auto-generated class. DTD syntax highlighting
- *
+ *
+ */
+
+/**
+ * Auto-generated class. DTD syntax highlighting
+ *
* @author Andrey Demenev
- * @category Text
- * @package Text_Highlighter
- * @copyright 2004-2006 Andrey Demenev
- * @license http://www.php.net/license/3_0.txt PHP License
- * @version Release: 0.7.0
- * @link http://pear.php.net/package/Text_Highlighter
- */
-class Text_Highlighter_DTD extends Text_Highlighter
-{
+ * @category Text
+ * @package Text_Highlighter
+ * @copyright 2004-2006 Andrey Demenev
+ * @license http://www.php.net/license/3_0.txt PHP License
+ * @version Release: 0.7.0
+ * @link http://pear.php.net/package/Text_Highlighter
+ */
+class Text_Highlighter_DTD extends Text_Highlighter
+{
var $_language = 'dtd';
- /**
- * Constructor
- *
- * @param array $options
- * @access public
- */
- function __construct($options=array())
- {
-
+ /**
+ * Constructor
+ *
+ * @param array $options
+ * @access public
+ */
+ function __construct($options=array())
+ {
+
$this->_options = $options;
$this->_regs = array (
-1 => '/(\\ * | Article_Category | * <----- * | Category |
- * +---------+ +------------------+ +----------+
- *
- * Where one article may have 0 or more categories and each category may have 0
- * or more articles. We may model Article-Category object relationship
- * as active record as follows.
- *
- * class ArticleRecord
- * {
- * const TABLE='Article';
- * public $article_id;
- *
- * public $Categories=array(); //foreign object collection.
- *
- * public static $RELATIONS = array
- * (
- * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * class CategoryRecord
- * {
- * const TABLE='Category';
- * public $category_id;
- *
- * public $Articles=array();
- *
- * public static $RELATIONS = array
- * (
- * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- *
- *
- * The static $RELATIONS property of ArticleRecord defines that the
- * property $Categories has many CategoryRecords. Similar, the
- * static $RELATIONS property of CategoryRecord defines many ArticleRecords.
- *
- * The articles with categories list may be fetched as follows.
- *
- * $articles = TeamRecord::finder()->withCategories()->findAll();
- *
- * The method with_xxx() (where xxx is the relationship property
- * name, in this case, Categories) fetchs the corresponding CategoryRecords using
- * a second query (not by using a join). The with_xxx() accepts the same
- * arguments as other finder methods of TActiveRecord.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasManyAssociation extends TActiveRecordRelation
-{
- private $_association;
- private $_sourceTable;
- private $_foreignTable;
- private $_association_columns=array();
-
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects using association table.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys();
- $properties = array_values($sourceKeys);
- $indexValues = $this->getIndexValues($properties, $results);
- $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
- }
-
- /**
- * @return array 2 arrays of source keys and foreign keys from the association table.
- */
- public function getRelationForeignKeys()
- {
- $association = $this->getAssociationTable();
- $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true);
- $fkObject = $this->getContext()->getForeignRecordFinder();
- $foreignKeys = $this->findForeignKeys($association, $fkObject);
- return array($sourceKeys, $foreignKeys);
- }
-
- /**
- * @return TDbTableInfo association table information.
- */
- protected function getAssociationTable()
- {
- if($this->_association===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $conn = $this->getSourceRecord()->getDbConnection();
- //table name may include the fk column name separated with a dot.
- $table = explode('.', $this->getContext()->getAssociationTable());
- if(count($table)>1)
- {
- $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]);
- $this->_association_columns = preg_split('/\s*[, ]\*/',$columns);
- }
- $this->_association = $gateway->getTableInfo($conn, $table[0]);
- }
- return $this->_association;
- }
-
- /**
- * @return TDbTableInfo source table information.
- */
- protected function getSourceTable()
- {
- if($this->_sourceTable===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord());
- }
- return $this->_sourceTable;
- }
-
- /**
- * @return TDbTableInfo foreign table information.
- */
- protected function getForeignTable()
- {
- if($this->_foreignTable===null)
- {
- $gateway = $this->getSourceRecord()->getRecordGateway();
- $fkObject = $this->getContext()->getForeignRecordFinder();
- $this->_foreignTable = $gateway->getRecordTableInfo($fkObject);
- }
- return $this->_foreignTable;
- }
-
- /**
- * @return TDataGatewayCommand
- */
- protected function getCommandBuilder()
- {
- return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
- }
-
- /**
- * @return TDataGatewayCommand
- */
- protected function getForeignCommandBuilder()
- {
- $obj = $this->getContext()->getForeignRecordFinder();
- return $this->getSourceRecord()->getRecordGateway()->getCommand($obj);
- }
-
-
- /**
- * Fetches the foreign objects using TActiveRecord::findAllByIndex()
- * @param array field names
- * @param array foreign key index values.
- */
- protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys)
- {
- $criteria = $this->getCriteria();
- $finder = $this->getContext()->getForeignRecordFinder();
- $type = get_class($finder);
- $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys);
- $srcProps = array_keys($sourceKeys);
- $collections=array();
- foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row)
- {
- $hash = $this->getObjectHash($row, $srcProps);
- foreach($srcProps as $column)
- unset($row[$column]);
- $obj = $this->createFkObject($type,$row,$foreignKeys);
- $collections[$hash][] = $obj;
- }
- $this->setResultCollection($results, $collections, array_values($sourceKeys));
- }
-
- /**
- * @param string active record class name.
- * @param array row data
- * @param array foreign key column names
- * @return TActiveRecord
- */
- protected function createFkObject($type,$row,$foreignKeys)
- {
- $obj = TActiveRecord::createRecord($type, $row);
- if(count($this->_association_columns) > 0)
- {
- $i=0;
- foreach($foreignKeys as $ref=>$fk)
- $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]);
- }
- return $obj;
- }
-
- /**
- * @param TSqlCriteria
- * @param TTableInfo association table info
- * @param array field names
- * @param array field values
- */
- public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys)
- {
- $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys);
- $fkTable = $this->getForeignTable()->getTableFullName();
- $srcColumns = $this->getSourceColumns($sourceKeys);
- if(($where=$criteria->getCondition())===null)
- $where='1=1';
- $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}";
-
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
-
- $builder = $this->getForeignCommandBuilder()->getBuilder();
- $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset);
- $this->getCommandBuilder()->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param array source table column names.
- * @return string comma separated source column names.
- */
- protected function getSourceColumns($sourceKeys)
- {
- $columns=array();
- $table = $this->getAssociationTable();
- $tableName = $table->getTableFullName();
- $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns);
- foreach($columnNames as $name)
- $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName();
- return implode(', ', $columns);
- }
-
- /**
- * SQL inner join for M-N relationship via association table.
- * @param array foreign table column key names.
- * @param array source table index values.
- * @param array source table column names.
- * @return string inner join condition for M-N relationship via association table.
- */
- protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
- {
- $refInfo= $this->getAssociationTable();
- $fkInfo = $this->getForeignTable();
-
- $refTable = $refInfo->getTableFullName();
- $fkTable = $fkInfo->getTableFullName();
-
- $joins = array();
- $hasAssociationColumns = count($this->_association_columns) > 0;
- $i=0;
- foreach($foreignKeys as $ref=>$fk)
- {
- if($hasAssociationColumns)
- $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName();
- else
- $refField = $refInfo->getColumn($ref)->getColumnName();
- $fkField = $fkInfo->getColumn($fk)->getColumnName();
- $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
- }
- $joinCondition = implode(' AND ', $joins);
- $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
- return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
- }
-
- /**
- * Updates the associated foreign objects.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $obj = $this->getContext()->getSourceRecord();
- $fkObjects = &$obj->{$this->getContext()->getProperty()};
- $success=true;
- if(($total = count($fkObjects))> 0)
- {
- $source = $this->getSourceRecord();
- $builder = $this->getAssociationTableCommandBuilder();
- for($i=0;$i<$total;$i++)
- $success = $fkObjects[$i]->save() && $success;
- return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success;
- }
- return $success;
- }
-
- /**
- * @return TDbCommandBuilder
- */
- protected function getAssociationTableCommandBuilder()
- {
- $conn = $this->getContext()->getSourceRecord()->getDbConnection();
- return $this->getAssociationTable()->createCommandBuilder($conn);
- }
-
- private function hasAssociationData($builder,$data)
- {
- $condition=array();
- $table = $this->getAssociationTable();
- foreach($data as $name=>$value)
- $condition[] = $table->getColumn($name)->getColumnName().' = ?';
- $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data));
- $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar()));
- return intval($result) > 0;
- }
-
- private function addAssociationData($builder,$data)
- {
- $command = $builder->createInsertCommand($data);
- return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0;
- }
-
- private function updateAssociationTable($obj,$fkObjects, $builder)
- {
- $source = $this->getSourceRecordValues($obj);
- $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]);
- $success=true;
- foreach($fkObjects as $fkObject)
- {
- $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject));
- if(!$this->hasAssociationData($builder,$data))
- $success = $this->addAssociationData($builder,$data) && $success;
- }
- return $success;
- }
-
- private function getSourceRecordValues($obj)
- {
- $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj);
- $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj);
- $data = array();
- $i=0;
- foreach($sourceKeys as $name=>$srcKey)
- $data[$name] = $indexValues[0][$i++];
- return $data;
- }
-
- private function getForeignObjectValues($foreignKeys,$fkObject)
- {
- $data=array();
- foreach($foreignKeys as $name=>$fKey)
- $data[$name] = $fkObject->getColumnValue($fKey);
- return $data;
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relations class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * Implements the M-N (many to many) relationship via association table.
+ * Consider the entity relationship between Articles and Categories
+ * via the association table Article_Category.
+ *
+ * +---------+ +------------------+ +----------+
+ * | Article | * -----> * | Article_Category | * <----- * | Category |
+ * +---------+ +------------------+ +----------+
+ *
+ * Where one article may have 0 or more categories and each category may have 0
+ * or more articles. We may model Article-Category object relationship
+ * as active record as follows.
+ *
+ * class ArticleRecord
+ * {
+ * const TABLE='Article';
+ * public $article_id;
+ *
+ * public $Categories=array(); //foreign object collection.
+ *
+ * public static $RELATIONS = array
+ * (
+ * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category')
+ * );
+ *
+ * public static function finder($className=__CLASS__)
+ * {
+ * return parent::finder($className);
+ * }
+ * }
+ * class CategoryRecord
+ * {
+ * const TABLE='Category';
+ * public $category_id;
+ *
+ * public $Articles=array();
+ *
+ * public static $RELATIONS = array
+ * (
+ * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category')
+ * );
+ *
+ * public static function finder($className=__CLASS__)
+ * {
+ * return parent::finder($className);
+ * }
+ * }
+ *
+ *
+ * The static $RELATIONS property of ArticleRecord defines that the
+ * property $Categories has many CategoryRecords. Similar, the
+ * static $RELATIONS property of CategoryRecord defines many ArticleRecords.
+ *
+ * The articles with categories list may be fetched as follows.
+ *
+ * $articles = TeamRecord::finder()->withCategories()->findAll();
+ *
+ * The method with_xxx() (where xxx is the relationship property
+ * name, in this case, Categories) fetchs the corresponding CategoryRecords using
+ * a second query (not by using a join). The with_xxx() accepts the same
+ * arguments as other finder methods of TActiveRecord.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordHasManyAssociation extends TActiveRecordRelation
+{
+ private $_association;
+ private $_sourceTable;
+ private $_foreignTable;
+ private $_association_columns=array();
+
+ /**
+ * Get the foreign key index values from the results and make calls to the
+ * database to find the corresponding foreign objects using association table.
+ * @param array original results.
+ */
+ protected function collectForeignObjects(&$results)
+ {
+ list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys();
+ $properties = array_values($sourceKeys);
+ $indexValues = $this->getIndexValues($properties, $results);
+ $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
+ }
+
+ /**
+ * @return array 2 arrays of source keys and foreign keys from the association table.
+ */
+ public function getRelationForeignKeys()
+ {
+ $association = $this->getAssociationTable();
+ $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true);
+ $fkObject = $this->getContext()->getForeignRecordFinder();
+ $foreignKeys = $this->findForeignKeys($association, $fkObject);
+ return array($sourceKeys, $foreignKeys);
+ }
+
+ /**
+ * @return TDbTableInfo association table information.
+ */
+ protected function getAssociationTable()
+ {
+ if($this->_association===null)
+ {
+ $gateway = $this->getSourceRecord()->getRecordGateway();
+ $conn = $this->getSourceRecord()->getDbConnection();
+ //table name may include the fk column name separated with a dot.
+ $table = explode('.', $this->getContext()->getAssociationTable());
+ if(count($table)>1)
+ {
+ $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]);
+ $this->_association_columns = preg_split('/\s*[, ]\*/',$columns);
+ }
+ $this->_association = $gateway->getTableInfo($conn, $table[0]);
+ }
+ return $this->_association;
+ }
+
+ /**
+ * @return TDbTableInfo source table information.
+ */
+ protected function getSourceTable()
+ {
+ if($this->_sourceTable===null)
+ {
+ $gateway = $this->getSourceRecord()->getRecordGateway();
+ $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord());
+ }
+ return $this->_sourceTable;
+ }
+
+ /**
+ * @return TDbTableInfo foreign table information.
+ */
+ protected function getForeignTable()
+ {
+ if($this->_foreignTable===null)
+ {
+ $gateway = $this->getSourceRecord()->getRecordGateway();
+ $fkObject = $this->getContext()->getForeignRecordFinder();
+ $this->_foreignTable = $gateway->getRecordTableInfo($fkObject);
+ }
+ return $this->_foreignTable;
+ }
+
+ /**
+ * @return TDataGatewayCommand
+ */
+ protected function getCommandBuilder()
+ {
+ return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
+ }
+
+ /**
+ * @return TDataGatewayCommand
+ */
+ protected function getForeignCommandBuilder()
+ {
+ $obj = $this->getContext()->getForeignRecordFinder();
+ return $this->getSourceRecord()->getRecordGateway()->getCommand($obj);
+ }
+
+
+ /**
+ * Fetches the foreign objects using TActiveRecord::findAllByIndex()
+ * @param array field names
+ * @param array foreign key index values.
+ */
+ protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys)
+ {
+ $criteria = $this->getCriteria();
+ $finder = $this->getContext()->getForeignRecordFinder();
+ $type = get_class($finder);
+ $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys);
+ $srcProps = array_keys($sourceKeys);
+ $collections=array();
+ foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row)
+ {
+ $hash = $this->getObjectHash($row, $srcProps);
+ foreach($srcProps as $column)
+ unset($row[$column]);
+ $obj = $this->createFkObject($type,$row,$foreignKeys);
+ $collections[$hash][] = $obj;
+ }
+ $this->setResultCollection($results, $collections, array_values($sourceKeys));
+ }
+
+ /**
+ * @param string active record class name.
+ * @param array row data
+ * @param array foreign key column names
+ * @return TActiveRecord
+ */
+ protected function createFkObject($type,$row,$foreignKeys)
+ {
+ $obj = TActiveRecord::createRecord($type, $row);
+ if(count($this->_association_columns) > 0)
+ {
+ $i=0;
+ foreach($foreignKeys as $ref=>$fk)
+ $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]);
+ }
+ return $obj;
+ }
+
+ /**
+ * @param TSqlCriteria
+ * @param TTableInfo association table info
+ * @param array field names
+ * @param array field values
+ */
+ public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys)
+ {
+ $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys);
+ $fkTable = $this->getForeignTable()->getTableFullName();
+ $srcColumns = $this->getSourceColumns($sourceKeys);
+ if(($where=$criteria->getCondition())===null)
+ $where='1=1';
+ $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}";
+
+ $parameters = $criteria->getParameters()->toArray();
+ $ordering = $criteria->getOrdersBy();
+ $limit = $criteria->getLimit();
+ $offset = $criteria->getOffset();
+
+ $builder = $this->getForeignCommandBuilder()->getBuilder();
+ $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset);
+ $this->getCommandBuilder()->onCreateCommand($command, $criteria);
+ return $command;
+ }
+
+ /**
+ * @param array source table column names.
+ * @return string comma separated source column names.
+ */
+ protected function getSourceColumns($sourceKeys)
+ {
+ $columns=array();
+ $table = $this->getAssociationTable();
+ $tableName = $table->getTableFullName();
+ $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns);
+ foreach($columnNames as $name)
+ $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName();
+ return implode(', ', $columns);
+ }
+
+ /**
+ * SQL inner join for M-N relationship via association table.
+ * @param array foreign table column key names.
+ * @param array source table index values.
+ * @param array source table column names.
+ * @return string inner join condition for M-N relationship via association table.
+ */
+ protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
+ {
+ $refInfo= $this->getAssociationTable();
+ $fkInfo = $this->getForeignTable();
+
+ $refTable = $refInfo->getTableFullName();
+ $fkTable = $fkInfo->getTableFullName();
+
+ $joins = array();
+ $hasAssociationColumns = count($this->_association_columns) > 0;
+ $i=0;
+ foreach($foreignKeys as $ref=>$fk)
+ {
+ if($hasAssociationColumns)
+ $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName();
+ else
+ $refField = $refInfo->getColumn($ref)->getColumnName();
+ $fkField = $fkInfo->getColumn($fk)->getColumnName();
+ $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
+ }
+ $joinCondition = implode(' AND ', $joins);
+ $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
+ return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
+ }
+
+ /**
+ * Updates the associated foreign objects.
+ * @return boolean true if all update are success (including if no update was required), false otherwise .
+ */
+ public function updateAssociatedRecords()
+ {
+ $obj = $this->getContext()->getSourceRecord();
+ $fkObjects = &$obj->{$this->getContext()->getProperty()};
+ $success=true;
+ if(($total = count($fkObjects))> 0)
+ {
+ $source = $this->getSourceRecord();
+ $builder = $this->getAssociationTableCommandBuilder();
+ for($i=0;$i<$total;$i++)
+ $success = $fkObjects[$i]->save() && $success;
+ return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success;
+ }
+ return $success;
+ }
+
+ /**
+ * @return TDbCommandBuilder
+ */
+ protected function getAssociationTableCommandBuilder()
+ {
+ $conn = $this->getContext()->getSourceRecord()->getDbConnection();
+ return $this->getAssociationTable()->createCommandBuilder($conn);
+ }
+
+ private function hasAssociationData($builder,$data)
+ {
+ $condition=array();
+ $table = $this->getAssociationTable();
+ foreach($data as $name=>$value)
+ $condition[] = $table->getColumn($name)->getColumnName().' = ?';
+ $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data));
+ $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar()));
+ return intval($result) > 0;
+ }
+
+ private function addAssociationData($builder,$data)
+ {
+ $command = $builder->createInsertCommand($data);
+ return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0;
+ }
+
+ private function updateAssociationTable($obj,$fkObjects, $builder)
+ {
+ $source = $this->getSourceRecordValues($obj);
+ $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]);
+ $success=true;
+ foreach($fkObjects as $fkObject)
+ {
+ $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject));
+ if(!$this->hasAssociationData($builder,$data))
+ $success = $this->addAssociationData($builder,$data) && $success;
+ }
+ return $success;
+ }
+
+ private function getSourceRecordValues($obj)
+ {
+ $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj);
+ $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj);
+ $data = array();
+ $i=0;
+ foreach($sourceKeys as $name=>$srcKey)
+ $data[$name] = $indexValues[0][$i++];
+ return $data;
+ }
+
+ private function getForeignObjectValues($foreignKeys,$fkObject)
+ {
+ $data=array();
+ foreach($foreignKeys as $name=>$fKey)
+ $data[$name] = $fkObject->getColumnValue($fKey);
+ return $data;
+ }
+}
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
index bc895901..a762e196 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
@@ -1,145 +1,145 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relationship class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * TActiveRecordHasOne models the object relationship that a record (the source object)
- * property is an instance of foreign record object having a foreign key
- * related to the source object. The HAS_ONE relation is very similar to the
- * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
- *
- * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
- * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
- * corresponding record.
- *
- * Consider the entity relationship between a Car and a Engine.
- *
- * +-----+ +--------+
- * | Car | 1 <----- 1 | Engine |
- * +-----+ +--------+
- *
- * Where each engine belongs to only one car, that is, the Engine entity has
- * a foreign key to the Car's primary key. We may model
- * Engine-Car object relationship as active record as follows.
- *
- * class CarRecord extends TActiveRecord
- * {
- * const TABLE='car';
- * public $car_id; //primary key
- * public $colour;
- *
- * public $engine; //engine foreign object
- *
- * public static $RELATIONS=array
- * (
- * 'engine' => array(self::HAS_ONE, 'EngineRecord')
- * );
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- * class EngineRecord extends TActiveRecord
- * {
- * const TABLE='engine';
- * public $engine_id;
- * public $capacity;
- * public $car_id; //foreign key to cars
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- *
- * The static $RELATIONS property of CarRecord defines that the
- * property $engine that will reference an EngineRecord instance.
- *
- * The car record with engine property list may be fetched as follows.
- *
- * $cars = CarRecord::finder()->with_engine()->findAll();
- *
- * The method with_xxx() (where xxx is the relationship property
- * name, in this case, engine) fetchs the corresponding EngineRecords using
- * a second query (not by using a join). The with_xxx() accepts the same
- * arguments as other finder methods of TActiveRecord, e.g. with_engine('capacity < ?', 3.8).
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasOne extends TActiveRecordRelation
-{
- /**
- * Get the foreign key index values from the results and make calls to the
- * database to find the corresponding foreign objects.
- * @param array original results.
- */
- protected function collectForeignObjects(&$results)
- {
- $fkeys = $this->getRelationForeignKeys();
- $properties = array_values($fkeys);
- $fields = array_keys($fkeys);
-
- $indexValues = $this->getIndexValues($properties, $results);
- $fkObjects = $this->findForeignObjects($fields,$indexValues);
- $this->populateResult($results,$properties,$fkObjects,$fields);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- $fkObject = $this->getContext()->getForeignRecordFinder();
- return $this->findForeignKeys($fkObject, $this->getSourceRecord());
- }
-
- /**
- * Sets the foreign objects to the given property on the source object.
- * @param TActiveRecord source object.
- * @param array foreign objects.
- */
- protected function setObjectProperty($source, $properties, &$collections)
- {
- $hash = $this->getObjectHash($source, $properties);
- $prop = $this->getContext()->getProperty();
- if(isset($collections[$hash]) && count($collections[$hash]) > 0)
- {
- if(count($collections[$hash]) > 1)
- throw new TActiveRecordException('ar_belongs_to_multiple_result');
- $source->setColumnValue($prop, $collections[$hash][0]);
- }
- }
-
- /**
- * Updates the associated foreign objects.
- * @return boolean true if all update are success (including if no update was required), false otherwise .
- */
- public function updateAssociatedRecords()
- {
- $fkObject = $this->getContext()->getPropertyValue();
- $source = $this->getSourceRecord();
- $fkeys = $this->findForeignKeys($fkObject, $source);
- foreach($fkeys as $fKey => $srcKey)
- $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey));
- return $fkObject->save();
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relationship class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * TActiveRecordHasOne models the object relationship that a record (the source object)
+ * property is an instance of foreign record object having a foreign key
+ * related to the source object. The HAS_ONE relation is very similar to the
+ * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
+ *
+ * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
+ * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
+ * corresponding record.
+ *
+ * Consider the entity relationship between a Car and a Engine.
+ *
+ * +-----+ +--------+
+ * | Car | 1 <----- 1 | Engine |
+ * +-----+ +--------+
+ *
+ * Where each engine belongs to only one car, that is, the Engine entity has
+ * a foreign key to the Car's primary key. We may model
+ * Engine-Car object relationship as active record as follows.
+ *
+ * class CarRecord extends TActiveRecord
+ * {
+ * const TABLE='car';
+ * public $car_id; //primary key
+ * public $colour;
+ *
+ * public $engine; //engine foreign object
+ *
+ * public static $RELATIONS=array
+ * (
+ * 'engine' => array(self::HAS_ONE, 'EngineRecord')
+ * );
+ *
+ * public static function finder($className=__CLASS__)
+ * {
+ * return parent::finder($className);
+ * }
+ * }
+ * class EngineRecord extends TActiveRecord
+ * {
+ * const TABLE='engine';
+ * public $engine_id;
+ * public $capacity;
+ * public $car_id; //foreign key to cars
+ *
+ * public static function finder($className=__CLASS__)
+ * {
+ * return parent::finder($className);
+ * }
+ * }
+ *
+ * The static $RELATIONS property of CarRecord defines that the
+ * property $engine that will reference an EngineRecord instance.
+ *
+ * The car record with engine property list may be fetched as follows.
+ *
+ * $cars = CarRecord::finder()->with_engine()->findAll();
+ *
+ * The method with_xxx() (where xxx is the relationship property
+ * name, in this case, engine) fetchs the corresponding EngineRecords using
+ * a second query (not by using a join). The with_xxx() accepts the same
+ * arguments as other finder methods of TActiveRecord, e.g. with_engine('capacity < ?', 3.8).
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordHasOne extends TActiveRecordRelation
+{
+ /**
+ * Get the foreign key index values from the results and make calls to the
+ * database to find the corresponding foreign objects.
+ * @param array original results.
+ */
+ protected function collectForeignObjects(&$results)
+ {
+ $fkeys = $this->getRelationForeignKeys();
+ $properties = array_values($fkeys);
+ $fields = array_keys($fkeys);
+
+ $indexValues = $this->getIndexValues($properties, $results);
+ $fkObjects = $this->findForeignObjects($fields,$indexValues);
+ $this->populateResult($results,$properties,$fkObjects,$fields);
+ }
+
+ /**
+ * @return array foreign key field names as key and object properties as value.
+ * @since 3.1.2
+ */
+ public function getRelationForeignKeys()
+ {
+ $fkObject = $this->getContext()->getForeignRecordFinder();
+ return $this->findForeignKeys($fkObject, $this->getSourceRecord());
+ }
+
+ /**
+ * Sets the foreign objects to the given property on the source object.
+ * @param TActiveRecord source object.
+ * @param array foreign objects.
+ */
+ protected function setObjectProperty($source, $properties, &$collections)
+ {
+ $hash = $this->getObjectHash($source, $properties);
+ $prop = $this->getContext()->getProperty();
+ if(isset($collections[$hash]) && count($collections[$hash]) > 0)
+ {
+ if(count($collections[$hash]) > 1)
+ throw new TActiveRecordException('ar_belongs_to_multiple_result');
+ $source->setColumnValue($prop, $collections[$hash][0]);
+ }
+ }
+
+ /**
+ * Updates the associated foreign objects.
+ * @return boolean true if all update are success (including if no update was required), false otherwise .
+ */
+ public function updateAssociatedRecords()
+ {
+ $fkObject = $this->getContext()->getPropertyValue();
+ $source = $this->getSourceRecord();
+ $fkeys = $this->findForeignKeys($fkObject, $source);
+ foreach($fkeys as $fKey => $srcKey)
+ $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey));
+ return $fkObject->save();
+ }
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
index 39482635..c59532d9 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
@@ -1,254 +1,254 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Load active record relationship context.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');
-
-/**
- * Base class for active record relationships.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-abstract class TActiveRecordRelation
-{
- private $_context;
- private $_criteria;
-
- public function __construct(TActiveRecordRelationContext $context, $criteria)
- {
- $this->_context = $context;
- $this->_criteria = $criteria;
- }
-
- /**
- * @return TActiveRecordRelationContext
- */
- protected function getContext()
- {
- return $this->_context;
- }
-
- /**
- * @return TActiveRecordCriteria
- */
- protected function getCriteria()
- {
- return $this->_criteria;
- }
-
- /**
- * @return TActiveRecord
- */
- protected function getSourceRecord()
- {
- return $this->getContext()->getSourceRecord();
- }
-
- abstract protected function collectForeignObjects(&$results);
-
- /**
- * Dispatch the method calls to the source record finder object. When
- * an instance of TActiveRecord or an array of TActiveRecord is returned
- * the corresponding foreign objects are also fetched and assigned.
- *
- * Multiple relationship calls can be chain together.
- *
- * @param string method name called
- * @param array method arguments
- * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called.
- */
- public function __call($method,$args)
- {
- static $stack=array();
-
- $results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
- $validArray = is_array($results) && count($results) > 0;
- if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
- {
- $this->collectForeignObjects($results);
- while($obj = array_pop($stack))
- $obj->collectForeignObjects($results);
- }
- else if($results instanceof TActiveRecordRelation)
- $stack[] = $this; //call it later
- else if($results === null || !$validArray)
- $stack = array();
- return $results;
- }
-
- /**
- * Fetch results for current relationship.
- * @return boolean always true.
- */
- public function fetchResultsInto($obj)
- {
- $this->collectForeignObjects($obj);
- return true;
- }
-
- /**
- * Returns foreign keys in $fromRecord with source column names as key
- * and foreign column names in the corresponding $matchesRecord as value.
- * The method returns the first matching foreign key between these 2 records.
- * @param TActiveRecord $fromRecord
- * @param TActiveRecord $matchesRecord
- * @return array foreign keys with source column names as key and foreign column names as value.
- */
- protected function findForeignKeys($from, $matchesRecord, $loose=false)
- {
- $gateway = $matchesRecord->getRecordGateway();
- $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
- $matchingTableName = strtolower($recordTableInfo->getTableName());
- $matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
- $tableInfo=$from;
- if($from instanceof TActiveRecord)
- $tableInfo = $gateway->getRecordTableInfo($from);
- //find first non-empty FK
- foreach($tableInfo->getForeignKeys() as $fkeys)
- {
- $fkTable = strtolower($fkeys['table']);
- if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
- {
- $hasFkField = !$loose && $this->getContext()->hasFkField();
- $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
- if(!empty($key))
- return $key;
- }
- }
-
- //none found
- $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName();
- throw new TActiveRecordException('ar_relations_missing_fk',
- $tableInfo->getTableFullName(), $matching);
- }
-
- /**
- * @return array foreign key field names as key and object properties as value.
- * @since 3.1.2
- */
- abstract public function getRelationForeignKeys();
-
- /**
- * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION.
- * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot
- * are ignored.
- */
- private function getFkFields($fkeys)
- {
- $matching = array();
- preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching);
- $fields = array();
- foreach($fkeys as $fkName => $field)
- {
- if(in_array($fkName, $matching[2]))
- $fields[$fkName] = $field;
- }
- return $fields;
- }
-
- /**
- * @param mixed object or array to be hashed
- * @param array name of property for hashing the properties.
- * @return string object hash using crc32 and serialize.
- */
- protected function getObjectHash($obj, $properties)
- {
- $ids=array();
- foreach($properties as $property)
- $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property];
- return serialize($ids);
- }
-
- /**
- * Fetches the foreign objects using TActiveRecord::findAllByIndex()
- * @param array field names
- * @param array foreign key index values.
- * @return TActiveRecord[] foreign objects.
- */
- protected function findForeignObjects($fields, $indexValues)
- {
- $finder = $this->getContext()->getForeignRecordFinder();
- return $finder->findAllByIndex($this->_criteria, $fields, $indexValues);
- }
-
- /**
- * Obtain the foreign key index values from the results.
- * @param array property names
- * @param array TActiveRecord results
- * @return array foreign key index values.
- */
- protected function getIndexValues($keys, $results)
- {
- if(!is_array($results) && !$results instanceof ArrayAccess)
- $results = array($results);
- $values=array();
- foreach($results as $result)
- {
- $value = array();
- foreach($keys as $name)
- $value[] = $result->getColumnValue($name);
- $values[] = $value;
- }
- return $values;
- }
-
- /**
- * Populate the results with the foreign objects found.
- * @param array source results
- * @param array source property names
- * @param array foreign objects
- * @param array foreign object field names.
- */
- protected function populateResult(&$results,$properties,&$fkObjects,$fields)
- {
- $collections=array();
- foreach($fkObjects as $fkObject)
- $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject;
- $this->setResultCollection($results, $collections, $properties);
- }
-
- /**
- * Populates the result array with foreign objects (matched using foreign key hashed property values).
- * @param array $results
- * @param array $collections
- * @param array property names
- */
- protected function setResultCollection(&$results, &$collections, $properties)
- {
- if(is_array($results) || $results instanceof ArrayAccess)
- {
- for($i=0,$k=count($results);$i<$k;$i++)
- $this->setObjectProperty($results[$i], $properties, $collections);
- }
- else
- $this->setObjectProperty($results, $properties, $collections);
- }
-
- /**
- * Sets the foreign objects to the given property on the source object.
- * @param TActiveRecord source object.
- * @param array source properties
- * @param array foreign objects.
- */
- protected function setObjectProperty($source, $properties, &$collections)
- {
- $hash = $this->getObjectHash($source, $properties);
- $prop = $this->getContext()->getProperty();
- $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array();
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Load active record relationship context.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');
+
+/**
+ * Base class for active record relationships.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+abstract class TActiveRecordRelation
+{
+ private $_context;
+ private $_criteria;
+
+ public function __construct(TActiveRecordRelationContext $context, $criteria)
+ {
+ $this->_context = $context;
+ $this->_criteria = $criteria;
+ }
+
+ /**
+ * @return TActiveRecordRelationContext
+ */
+ protected function getContext()
+ {
+ return $this->_context;
+ }
+
+ /**
+ * @return TActiveRecordCriteria
+ */
+ protected function getCriteria()
+ {
+ return $this->_criteria;
+ }
+
+ /**
+ * @return TActiveRecord
+ */
+ protected function getSourceRecord()
+ {
+ return $this->getContext()->getSourceRecord();
+ }
+
+ abstract protected function collectForeignObjects(&$results);
+
+ /**
+ * Dispatch the method calls to the source record finder object. When
+ * an instance of TActiveRecord or an array of TActiveRecord is returned
+ * the corresponding foreign objects are also fetched and assigned.
+ *
+ * Multiple relationship calls can be chain together.
+ *
+ * @param string method name called
+ * @param array method arguments
+ * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called.
+ */
+ public function __call($method,$args)
+ {
+ static $stack=array();
+
+ $results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
+ $validArray = is_array($results) && count($results) > 0;
+ if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
+ {
+ $this->collectForeignObjects($results);
+ while($obj = array_pop($stack))
+ $obj->collectForeignObjects($results);
+ }
+ else if($results instanceof TActiveRecordRelation)
+ $stack[] = $this; //call it later
+ else if($results === null || !$validArray)
+ $stack = array();
+ return $results;
+ }
+
+ /**
+ * Fetch results for current relationship.
+ * @return boolean always true.
+ */
+ public function fetchResultsInto($obj)
+ {
+ $this->collectForeignObjects($obj);
+ return true;
+ }
+
+ /**
+ * Returns foreign keys in $fromRecord with source column names as key
+ * and foreign column names in the corresponding $matchesRecord as value.
+ * The method returns the first matching foreign key between these 2 records.
+ * @param TActiveRecord $fromRecord
+ * @param TActiveRecord $matchesRecord
+ * @return array foreign keys with source column names as key and foreign column names as value.
+ */
+ protected function findForeignKeys($from, $matchesRecord, $loose=false)
+ {
+ $gateway = $matchesRecord->getRecordGateway();
+ $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
+ $matchingTableName = strtolower($recordTableInfo->getTableName());
+ $matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
+ $tableInfo=$from;
+ if($from instanceof TActiveRecord)
+ $tableInfo = $gateway->getRecordTableInfo($from);
+ //find first non-empty FK
+ foreach($tableInfo->getForeignKeys() as $fkeys)
+ {
+ $fkTable = strtolower($fkeys['table']);
+ if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
+ {
+ $hasFkField = !$loose && $this->getContext()->hasFkField();
+ $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
+ if(!empty($key))
+ return $key;
+ }
+ }
+
+ //none found
+ $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName();
+ throw new TActiveRecordException('ar_relations_missing_fk',
+ $tableInfo->getTableFullName(), $matching);
+ }
+
+ /**
+ * @return array foreign key field names as key and object properties as value.
+ * @since 3.1.2
+ */
+ abstract public function getRelationForeignKeys();
+
+ /**
+ * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION.
+ * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot
+ * are ignored.
+ */
+ private function getFkFields($fkeys)
+ {
+ $matching = array();
+ preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching);
+ $fields = array();
+ foreach($fkeys as $fkName => $field)
+ {
+ if(in_array($fkName, $matching[2]))
+ $fields[$fkName] = $field;
+ }
+ return $fields;
+ }
+
+ /**
+ * @param mixed object or array to be hashed
+ * @param array name of property for hashing the properties.
+ * @return string object hash using crc32 and serialize.
+ */
+ protected function getObjectHash($obj, $properties)
+ {
+ $ids=array();
+ foreach($properties as $property)
+ $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property];
+ return serialize($ids);
+ }
+
+ /**
+ * Fetches the foreign objects using TActiveRecord::findAllByIndex()
+ * @param array field names
+ * @param array foreign key index values.
+ * @return TActiveRecord[] foreign objects.
+ */
+ protected function findForeignObjects($fields, $indexValues)
+ {
+ $finder = $this->getContext()->getForeignRecordFinder();
+ return $finder->findAllByIndex($this->_criteria, $fields, $indexValues);
+ }
+
+ /**
+ * Obtain the foreign key index values from the results.
+ * @param array property names
+ * @param array TActiveRecord results
+ * @return array foreign key index values.
+ */
+ protected function getIndexValues($keys, $results)
+ {
+ if(!is_array($results) && !$results instanceof ArrayAccess)
+ $results = array($results);
+ $values=array();
+ foreach($results as $result)
+ {
+ $value = array();
+ foreach($keys as $name)
+ $value[] = $result->getColumnValue($name);
+ $values[] = $value;
+ }
+ return $values;
+ }
+
+ /**
+ * Populate the results with the foreign objects found.
+ * @param array source results
+ * @param array source property names
+ * @param array foreign objects
+ * @param array foreign object field names.
+ */
+ protected function populateResult(&$results,$properties,&$fkObjects,$fields)
+ {
+ $collections=array();
+ foreach($fkObjects as $fkObject)
+ $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject;
+ $this->setResultCollection($results, $collections, $properties);
+ }
+
+ /**
+ * Populates the result array with foreign objects (matched using foreign key hashed property values).
+ * @param array $results
+ * @param array $collections
+ * @param array property names
+ */
+ protected function setResultCollection(&$results, &$collections, $properties)
+ {
+ if(is_array($results) || $results instanceof ArrayAccess)
+ {
+ for($i=0,$k=count($results);$i<$k;$i++)
+ $this->setObjectProperty($results[$i], $properties, $collections);
+ }
+ else
+ $this->setObjectProperty($results, $properties, $collections);
+ }
+
+ /**
+ * Sets the foreign objects to the given property on the source object.
+ * @param TActiveRecord source object.
+ * @param array source properties
+ * @param array foreign objects.
+ */
+ protected function setObjectProperty($source, $properties, &$collections)
+ {
+ $hash = $this->getObjectHash($source, $properties);
+ $prop = $this->getContext()->getProperty();
+ $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array();
+ }
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
index 8826bb69..dbcc097b 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
@@ -1,230 +1,230 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * TActiveRecordRelationContext holds information regarding record relationships
- * such as record relation property name, query criteria and foreign object record
- * class names.
- *
- * This class is use internally by passing a context to the TActiveRecordRelation
- * constructor.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordRelationContext
-{
- private $_property;
- private $_record;
- private $_relation; //data from an entry of TActiveRecord::$RELATION
- private $_fkeys;
-
- public function __construct($record, $property=null, $relation=null)
- {
- $this->_record=$record;
- $this->_property=$property;
- $this->_relation=$relation;
- }
-
- /**
- * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
- * @since 3.1.2
- */
- public function hasRecordRelation()
- {
- return $this->_relation!==null;
- }
-
- public function getPropertyValue()
- {
- $obj = $this->getSourceRecord();
- return $obj->getColumnValue($this->getProperty());
- }
-
- /**
- * @return string name of the record property that the relationship results will be assigned to.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @return TActiveRecord the active record instance that queried for its related records.
- */
- public function getSourceRecord()
- {
- return $this->_record;
- }
-
- /**
- * @return array foreign key of this relations, the keys is dependent on the
- * relationship type.
- * @since 3.1.2
- */
- public function getRelationForeignKeys()
- {
- if($this->_fkeys===null)
- $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
- return $this->_fkeys;
- }
-
- /**
- * @return string HAS_MANY, HAS_ONE, or BELONGS_TO
- */
- public function getRelationType()
- {
- return $this->_relation[0];
- }
-
- /**
- * @return string foreign record class name.
- */
- public function getForeignRecordClass()
- {
- return $this->_relation[1];
- }
-
- /**
- * @return string foreign key field names, comma delimited.
- * @since 3.1.2
- */
- public function getFkField()
- {
- return $this->_relation[2];
- }
-
- /**
- * @return string the query condition for the relation as specified in RELATIONS
- * @since 3.1.2
- */
- public function getCondition()
- {
- return isset($this->_relation[3])?$this->_relation[3]:null;
- }
-
- /**
- * @return array the query parameters for the relation as specified in RELATIONS
- * @since 3.1.2
- */
- public function getParameters()
- {
- return isset($this->_relation[4])?$this->_relation[4]:array();
- }
-
- /**
- * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
- * @since 3.1.2
- */
- public function hasFkField()
- {
- $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
- return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
- }
-
- /**
- * @return string the M-N relationship association table name.
- */
- public function getAssociationTable()
- {
- return $this->_relation[2];
- }
-
- /**
- * @return boolean true if the relationship is HAS_MANY and requires an association table.
- */
- public function hasAssociationTable()
- {
- $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
- return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
- }
-
- /**
- * @return TActiveRecord corresponding relationship foreign object finder instance.
- */
- public function getForeignRecordFinder()
- {
- return TActiveRecord::finder($this->getForeignRecordClass());
- }
-
- /**
- * Creates and return the TActiveRecordRelation handler for specific relationships.
- * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
- * or TActiveRecordHasManyAssocation will be returned.
- * @param TActiveRecordCriteria search criteria
- * @return TActiveRecordRelation record relationship handler instnace.
- * @throws TActiveRecordException if property is not defined or missing.
- */
- public function getRelationHandler($criteria=null)
- {
- if(!$this->hasRecordRelation())
- {
- throw new TActiveRecordException('ar_undefined_relation_prop',
- $this->_property, get_class($this->_record), 'RELATIONS');
- }
- if($criteria===null)
- $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
- switch($this->getRelationType())
- {
- case TActiveRecord::HAS_MANY:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
- return new TActiveRecordHasMany($this, $criteria);
- case TActiveRecord::MANY_TO_MANY:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
- return new TActiveRecordHasManyAssociation($this, $criteria);
- case TActiveRecord::HAS_ONE:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
- return new TActiveRecordHasOne($this, $criteria);
- case TActiveRecord::BELONGS_TO:
- Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
- return new TActiveRecordBelongsTo($this, $criteria);
- default:
- throw new TActiveRecordException('ar_invalid_relationship');
- }
- }
-
- /**
- * @return TActiveRecordRelationCommand
- */
- public function updateAssociatedRecords($updateBelongsTo=false)
- {
- $success=true;
- foreach($this->_record->getRecordRelations() as $data)
- {
- list($property, $relation) = $data;
- $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
- if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
- {
- $obj = $this->getSourceRecord();
- if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
- {
- $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
- $success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
- }
- }
- }
- return $success;
- }
-
- protected function isEmptyFkObject($obj)
- {
- if(is_object($obj))
- return $obj instanceof TList ? $obj->count() === 0 : false;
- else if(is_array($obj))
- return count($obj)===0;
- else
- return empty($obj);
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * TActiveRecordRelationContext holds information regarding record relationships
+ * such as record relation property name, query criteria and foreign object record
+ * class names.
+ *
+ * This class is use internally by passing a context to the TActiveRecordRelation
+ * constructor.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordRelationContext
+{
+ private $_property;
+ private $_record;
+ private $_relation; //data from an entry of TActiveRecord::$RELATION
+ private $_fkeys;
+
+ public function __construct($record, $property=null, $relation=null)
+ {
+ $this->_record=$record;
+ $this->_property=$property;
+ $this->_relation=$relation;
+ }
+
+ /**
+ * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
+ * @since 3.1.2
+ */
+ public function hasRecordRelation()
+ {
+ return $this->_relation!==null;
+ }
+
+ public function getPropertyValue()
+ {
+ $obj = $this->getSourceRecord();
+ return $obj->getColumnValue($this->getProperty());
+ }
+
+ /**
+ * @return string name of the record property that the relationship results will be assigned to.
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @return TActiveRecord the active record instance that queried for its related records.
+ */
+ public function getSourceRecord()
+ {
+ return $this->_record;
+ }
+
+ /**
+ * @return array foreign key of this relations, the keys is dependent on the
+ * relationship type.
+ * @since 3.1.2
+ */
+ public function getRelationForeignKeys()
+ {
+ if($this->_fkeys===null)
+ $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
+ return $this->_fkeys;
+ }
+
+ /**
+ * @return string HAS_MANY, HAS_ONE, or BELONGS_TO
+ */
+ public function getRelationType()
+ {
+ return $this->_relation[0];
+ }
+
+ /**
+ * @return string foreign record class name.
+ */
+ public function getForeignRecordClass()
+ {
+ return $this->_relation[1];
+ }
+
+ /**
+ * @return string foreign key field names, comma delimited.
+ * @since 3.1.2
+ */
+ public function getFkField()
+ {
+ return $this->_relation[2];
+ }
+
+ /**
+ * @return string the query condition for the relation as specified in RELATIONS
+ * @since 3.1.2
+ */
+ public function getCondition()
+ {
+ return isset($this->_relation[3])?$this->_relation[3]:null;
+ }
+
+ /**
+ * @return array the query parameters for the relation as specified in RELATIONS
+ * @since 3.1.2
+ */
+ public function getParameters()
+ {
+ return isset($this->_relation[4])?$this->_relation[4]:array();
+ }
+
+ /**
+ * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
+ * @since 3.1.2
+ */
+ public function hasFkField()
+ {
+ $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
+ return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
+ }
+
+ /**
+ * @return string the M-N relationship association table name.
+ */
+ public function getAssociationTable()
+ {
+ return $this->_relation[2];
+ }
+
+ /**
+ * @return boolean true if the relationship is HAS_MANY and requires an association table.
+ */
+ public function hasAssociationTable()
+ {
+ $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
+ return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
+ }
+
+ /**
+ * @return TActiveRecord corresponding relationship foreign object finder instance.
+ */
+ public function getForeignRecordFinder()
+ {
+ return TActiveRecord::finder($this->getForeignRecordClass());
+ }
+
+ /**
+ * Creates and return the TActiveRecordRelation handler for specific relationships.
+ * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
+ * or TActiveRecordHasManyAssocation will be returned.
+ * @param TActiveRecordCriteria search criteria
+ * @return TActiveRecordRelation record relationship handler instnace.
+ * @throws TActiveRecordException if property is not defined or missing.
+ */
+ public function getRelationHandler($criteria=null)
+ {
+ if(!$this->hasRecordRelation())
+ {
+ throw new TActiveRecordException('ar_undefined_relation_prop',
+ $this->_property, get_class($this->_record), 'RELATIONS');
+ }
+ if($criteria===null)
+ $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
+ switch($this->getRelationType())
+ {
+ case TActiveRecord::HAS_MANY:
+ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
+ return new TActiveRecordHasMany($this, $criteria);
+ case TActiveRecord::MANY_TO_MANY:
+ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
+ return new TActiveRecordHasManyAssociation($this, $criteria);
+ case TActiveRecord::HAS_ONE:
+ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
+ return new TActiveRecordHasOne($this, $criteria);
+ case TActiveRecord::BELONGS_TO:
+ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
+ return new TActiveRecordBelongsTo($this, $criteria);
+ default:
+ throw new TActiveRecordException('ar_invalid_relationship');
+ }
+ }
+
+ /**
+ * @return TActiveRecordRelationCommand
+ */
+ public function updateAssociatedRecords($updateBelongsTo=false)
+ {
+ $success=true;
+ foreach($this->_record->getRecordRelations() as $data)
+ {
+ list($property, $relation) = $data;
+ $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
+ if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
+ {
+ $obj = $this->getSourceRecord();
+ if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
+ {
+ $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
+ $success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
+ }
+ }
+ }
+ return $success;
+ }
+
+ protected function isEmptyFkObject($obj)
+ {
+ if(is_object($obj))
+ return $obj instanceof TList ? $obj->count() === 0 : false;
+ else if(is_array($obj))
+ return count($obj)===0;
+ else
+ return empty($obj);
+ }
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
index d2e58bc3..68f5806a 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
@@ -1,207 +1,207 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Include the base Active Record class.
- */
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-
-/**
- * Base class for Active Record scaffold views.
- *
- * Provides common properties for all scaffold views (such as, TScaffoldListView,
- * TScaffoldEditView, TScaffoldListView and TScaffoldView).
- *
- * During the OnPrRender stage the default css style file (filename style.css)
- * is published and registered. To override the default style, provide your own stylesheet
- * file explicitly.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-abstract class TScaffoldBase extends TTemplateControl
-{
- /**
- * @var TActiveRecord record instance (may be new or retrieved from db)
- */
- private $_record;
-
- /**
- * @return TDbMetaData table/view information
- */
- protected function getTableInfo()
- {
- $finder = $this->getRecordFinder();
- $gateway = $finder->getRecordManager()->getRecordGateWay();
- return $gateway->getRecordTableInfo($finder);
- }
-
- /**
- * @param TActiveRecord record instance
- * @return array record property values
- */
- protected function getRecordPropertyValues($record)
- {
- $data = array();
- foreach($this->getTableInfo()->getColumns() as $name=>$column)
- $data[] = $record->getColumnValue($name);
- return $data;
- }
-
- /**
- * @param TActiveRecord record instance
- * @return array record primary key values.
- */
- protected function getRecordPkValues($record)
- {
- $data=array();
- foreach($this->getTableInfo()->getColumns() as $name=>$column)
- {
- if($column->getIsPrimaryKey())
- $data[] = $record->getColumnValue($name);
- }
- return $data;
- }
-
- /**
- * Name of the Active Record class to be viewed or scaffolded.
- * @return string Active Record class name.
- */
- public function getRecordClass()
- {
- return $this->getViewState('RecordClass');
- }
-
- /**
- * Name of the Active Record class to be viewed or scaffolded.
- * @param string Active Record class name.
- */
- public function setRecordClass($value)
- {
- $this->setViewState('RecordClass', $value);
- }
-
- /**
- * Copy the view details from another scaffold view instance.
- * @param TScaffoldBase scaffold view.
- */
- protected function copyFrom(TScaffoldBase $obj)
- {
- $this->_record = $obj->_record;
- $this->setRecordClass($obj->getRecordClass());
- $this->setEnableDefaultStyle($obj->getEnableDefaultStyle());
- }
-
- /**
- * Unset the current record instance and table information.
- */
- protected function clearRecordObject()
- {
- $this->_record=null;
- }
-
- /**
- * Gets the current Active Record instance. Creates new instance if the
- * primary key value is null otherwise the record is fetched from the db.
- * @param array primary key value
- * @return TActiveRecord record instance
- */
- protected function getRecordObject($pk=null)
- {
- if($this->_record===null)
- {
- if($pk!==null)
- {
- $this->_record=$this->getRecordFinder()->findByPk($pk);
- if($this->_record===null)
- throw new TConfigurationException('scaffold_invalid_record_pk',
- $this->getRecordClass(), $pk);
- }
- else
- {
- $class = $this->getRecordClass();
- if($class!==null)
- $this->_record=Prado::createComponent($class);
- else
- {
- throw new TConfigurationException('scaffold_invalid_record_class',
- $this->getRecordClass(),$this->getID());
- }
- }
- }
- return $this->_record;
- }
-
- /**
- * @param TActiveRecord Active Record instance.
- */
- protected function setRecordObject(TActiveRecord $value)
- {
- $this->_record=$value;
- }
-
- /**
- * @return TActiveRecord Active Record finder instance
- */
- protected function getRecordFinder()
- {
- return TActiveRecord::finder($this->getRecordClass());
- }
-
- /**
- * @return string default scaffold stylesheet name
- */
- public function getDefaultStyle()
- {
- return $this->getViewState('DefaultStyle', 'style');
- }
-
- /**
- * @param string default scaffold stylesheet name
- */
- public function setDefaultStyle($value)
- {
- $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
- }
-
- /**
- * @return boolean enable default stylesheet, default is true.
- */
- public function getEnableDefaultStyle()
- {
- return $this->getViewState('EnableDefaultStyle', true);
- }
-
- /**
- * @param boolean enable default stylesheet, default is true.
- */
- public function setEnableDefaultStyle($value)
- {
- return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true);
- }
-
- /**
- * Publish the default stylesheet file.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- if($this->getEnableDefaultStyle())
- {
- $url = $this->publishAsset($this->getDefaultStyle().'.css');
- $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
- }
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Include the base Active Record class.
+ */
+Prado::using('System.Data.ActiveRecord.TActiveRecord');
+
+/**
+ * Base class for Active Record scaffold views.
+ *
+ * Provides common properties for all scaffold views (such as, TScaffoldListView,
+ * TScaffoldEditView, TScaffoldListView and TScaffoldView).
+ *
+ * During the OnPrRender stage the default css style file (filename style.css)
+ * is published and registered. To override the default style, provide your own stylesheet
+ * file explicitly.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+abstract class TScaffoldBase extends TTemplateControl
+{
+ /**
+ * @var TActiveRecord record instance (may be new or retrieved from db)
+ */
+ private $_record;
+
+ /**
+ * @return TDbMetaData table/view information
+ */
+ protected function getTableInfo()
+ {
+ $finder = $this->getRecordFinder();
+ $gateway = $finder->getRecordManager()->getRecordGateWay();
+ return $gateway->getRecordTableInfo($finder);
+ }
+
+ /**
+ * @param TActiveRecord record instance
+ * @return array record property values
+ */
+ protected function getRecordPropertyValues($record)
+ {
+ $data = array();
+ foreach($this->getTableInfo()->getColumns() as $name=>$column)
+ $data[] = $record->getColumnValue($name);
+ return $data;
+ }
+
+ /**
+ * @param TActiveRecord record instance
+ * @return array record primary key values.
+ */
+ protected function getRecordPkValues($record)
+ {
+ $data=array();
+ foreach($this->getTableInfo()->getColumns() as $name=>$column)
+ {
+ if($column->getIsPrimaryKey())
+ $data[] = $record->getColumnValue($name);
+ }
+ return $data;
+ }
+
+ /**
+ * Name of the Active Record class to be viewed or scaffolded.
+ * @return string Active Record class name.
+ */
+ public function getRecordClass()
+ {
+ return $this->getViewState('RecordClass');
+ }
+
+ /**
+ * Name of the Active Record class to be viewed or scaffolded.
+ * @param string Active Record class name.
+ */
+ public function setRecordClass($value)
+ {
+ $this->setViewState('RecordClass', $value);
+ }
+
+ /**
+ * Copy the view details from another scaffold view instance.
+ * @param TScaffoldBase scaffold view.
+ */
+ protected function copyFrom(TScaffoldBase $obj)
+ {
+ $this->_record = $obj->_record;
+ $this->setRecordClass($obj->getRecordClass());
+ $this->setEnableDefaultStyle($obj->getEnableDefaultStyle());
+ }
+
+ /**
+ * Unset the current record instance and table information.
+ */
+ protected function clearRecordObject()
+ {
+ $this->_record=null;
+ }
+
+ /**
+ * Gets the current Active Record instance. Creates new instance if the
+ * primary key value is null otherwise the record is fetched from the db.
+ * @param array primary key value
+ * @return TActiveRecord record instance
+ */
+ protected function getRecordObject($pk=null)
+ {
+ if($this->_record===null)
+ {
+ if($pk!==null)
+ {
+ $this->_record=$this->getRecordFinder()->findByPk($pk);
+ if($this->_record===null)
+ throw new TConfigurationException('scaffold_invalid_record_pk',
+ $this->getRecordClass(), $pk);
+ }
+ else
+ {
+ $class = $this->getRecordClass();
+ if($class!==null)
+ $this->_record=Prado::createComponent($class);
+ else
+ {
+ throw new TConfigurationException('scaffold_invalid_record_class',
+ $this->getRecordClass(),$this->getID());
+ }
+ }
+ }
+ return $this->_record;
+ }
+
+ /**
+ * @param TActiveRecord Active Record instance.
+ */
+ protected function setRecordObject(TActiveRecord $value)
+ {
+ $this->_record=$value;
+ }
+
+ /**
+ * @return TActiveRecord Active Record finder instance
+ */
+ protected function getRecordFinder()
+ {
+ return TActiveRecord::finder($this->getRecordClass());
+ }
+
+ /**
+ * @return string default scaffold stylesheet name
+ */
+ public function getDefaultStyle()
+ {
+ return $this->getViewState('DefaultStyle', 'style');
+ }
+
+ /**
+ * @param string default scaffold stylesheet name
+ */
+ public function setDefaultStyle($value)
+ {
+ $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
+ }
+
+ /**
+ * @return boolean enable default stylesheet, default is true.
+ */
+ public function getEnableDefaultStyle()
+ {
+ return $this->getViewState('EnableDefaultStyle', true);
+ }
+
+ /**
+ * @param boolean enable default stylesheet, default is true.
+ */
+ public function setEnableDefaultStyle($value)
+ {
+ return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true);
+ }
+
+ /**
+ * Publish the default stylesheet file.
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ if($this->getEnableDefaultStyle())
+ {
+ $url = $this->publishAsset($this->getDefaultStyle().'.css');
+ $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
+ }
+ }
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
index c0547627..166e3360 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
@@ -1,309 +1,309 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * Template control for editing an Active Record instance.
- * The RecordClass determines the Active Record class to be edited.
- * A particular record can be edited by specifying the {@link setRecordPk RecordPk}
- * value (may be an array for composite keys).
- *
- * The default editor input controls are created based on the column types.
- * The editor layout can be specified by a renderer by set the value
- * of the {@link setEditRenderer EditRenderer} property to the class name of a
- * class that implements TScaffoldEditRenderer. A renderer is an external
- * template control that implements IScaffoldEditRenderer.
- *
- * The Data of the IScaffoldEditRenderer will be set as the current Active
- * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer
- * is called when request to save the record is requested.
- *
- * Validators in the custom external editor template should have the
- * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the
- * value of the {@link getValidationGroup} of the TScaffoldEditView instance
- * (the edit view instance is the Parent of the IScaffoldEditRenderer in most
- * cases.
- *
- * Cosmetic changes to the default editor should be done using Cascading Stylesheets.
- * For example, a particular field/property can be hidden by specifying "display:none" for
- * the corresponding style (each field/property has unique Css class name as "property_xxx", where
- * xxx is the property name).
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldEditView extends TScaffoldBase
-{
- /**
- * @var IScaffoldEditRenderer custom scaffold edit renderer
- */
- private $_editRenderer;
-
- /**
- * Initialize the editor form if it is Visible.
- */
- public function onLoad($param)
- {
- if($this->getVisible())
- $this->initializeEditForm();
- }
-
- /**
- * @return string the class name for scaffold editor. Defaults to empty, meaning not set.
- */
- public function getEditRenderer()
- {
- return $this->getViewState('EditRenderer', '');
- }
-
- /**
- * @param string the class name for scaffold editor. Defaults to empty, meaning not set.
- */
- public function setEditRenderer($value)
- {
- $this->setViewState('EditRenderer', $value, '');
- }
-
- /**
- * @param array Active Record primary key value to be edited.
- */
- public function setRecordPk($value)
- {
- $this->clearRecordObject();
- $val = TPropertyValue::ensureArray($value);
- $this->setViewState('PK', count($val) > 0 ? $val : null);
- }
-
- /**
- * @return array Active Record primary key value.
- */
- public function getRecordPk()
- {
- return $this->getViewState('PK');
- }
-
- /**
- * @return TActiveRecord current Active Record instance
- */
- protected function getCurrentRecord()
- {
- return $this->getRecordObject($this->getRecordPk());
- }
-
- /**
- * Initialize the editor form
- */
- public function initializeEditForm()
- {
- $record = $this->getCurrentRecord();
- $classPath = $this->getEditRenderer();
- if($classPath === '')
- {
- $columns = $this->getTableInfo()->getColumns();
- $this->getInputRepeater()->setDataSource($columns);
- $this->getInputRepeater()->dataBind();
- }
- else
- {
- if($this->_editRenderer===null)
- $this->createEditRenderer($record, $classPath);
- else
- $this->_editRenderer->setData($record);
- }
- }
-
- /**
- * Instantiate the external edit renderer.
- * @param TActiveRecord record to be edited
- * @param string external edit renderer class name.
- * @throws TConfigurationException raised when renderer is not an
- * instance of IScaffoldEditRenderer.
- */
- protected function createEditRenderer($record, $classPath)
- {
- $this->_editRenderer = Prado::createComponent($classPath);
- if($this->_editRenderer instanceof IScaffoldEditRenderer)
- {
- $index = $this->getControls()->remove($this->getInputRepeater());
- $this->getControls()->insertAt($index,$this->_editRenderer);
- $this->_editRenderer->setData($record);
- }
- else
- {
- throw new TConfigurationException(
- 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record));
- }
- }
-
- /**
- * Initialize the default editor using the scaffold input builder.
- */
- protected function createRepeaterEditItem($sender, $param)
- {
- $type = $param->getItem()->getItemType();
- if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
- {
- $item = $param->getItem();
- $column = $item->getDataItem();
- if($column===null)
- return;
-
- $record = $this->getCurrentRecord();
- $builder = $this->getScaffoldInputBuilder($record);
- $builder->createScaffoldInput($this, $item, $column, $record);
- }
- }
-
- /**
- * Bubble the command name event. Stops bubbling when the page validator false.
- * Otherwise, the bubble event is continued.
- */
- public function bubbleEvent($sender, $param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'save':
- return $this->doSave() ? false : true;
- case 'clear':
- $this->setRecordPk(null);
- $this->initializeEditForm();
- return false;
- default:
- return false;
- }
- }
-
- /**
- * Check the validators, then tries to save the record.
- * @return boolean true if the validators are true, false otherwise.
- */
- protected function doSave()
- {
- if($this->getPage()->getIsValid())
- {
- $record = $this->getCurrentRecord();
- if($this->_editRenderer===null)
- {
- $table = $this->getTableInfo();
- $builder = $this->getScaffoldInputBuilder($record);
- foreach($this->getInputRepeater()->getItems() as $item)
- {
- $column = $table->getColumn($item->getCustomData());
- $builder->loadScaffoldInput($this, $item, $column, $record);
- }
- }
- else
- {
- $this->_editRenderer->updateRecord($record);
- }
- $record->save();
- return true;
- }
- else if($this->_editRenderer!==null)
- {
- //preserve the form data.
- $this->_editRenderer->updateRecord($this->getCurrentRecord());
- }
-
- return false;
- }
-
- /**
- * @return TRepeater default editor input controls repeater
- */
- protected function getInputRepeater()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_repeater');
- }
-
- /**
- * @return TButton Button triggered to save the Active Record.
- */
- public function getSaveButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_save');
- }
-
- /**
- * @return TButton Button to clear the editor inputs.
- */
- public function getClearButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_clear');
- }
-
- /**
- * @return TButton Button to cancel the edit action (e.g. hide the edit view).
- */
- public function getCancelButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_cancel');
- }
-
- /**
- * Create the default scaffold editor control factory.
- * @param TActiveRecord record instance.
- * @return TScaffoldInputBase scaffold editor control factory.
- */
- protected function getScaffoldInputBuilder($record)
- {
- static $_builders=array();
- $class = get_class($record);
- if(!isset($_builders[$class]))
- {
- Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
- $_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
- }
- return $_builders[$class];
- }
-
- /**
- * @return string editor validation group name.
- */
- public function getValidationGroup()
- {
- return 'group_'.$this->getUniqueID();
- }
-}
-
-/**
- * IScaffoldEditRenderer interface.
- *
- * IScaffoldEditRenderer defines the interface that an edit renderer
- * needs to implement. Besides the {@link getData Data} property, an edit
- * renderer also needs to provide {@link updateRecord updateRecord} method
- * that is called before the save() method is called on the TActiveRecord.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-interface IScaffoldEditRenderer extends IDataRenderer
-{
- /**
- * This method should update the record with the user input data.
- * @param TActiveRecord record to be saved.
- */
- public function updateRecord($record);
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Load scaffold base.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * Template control for editing an Active Record instance.
+ * The RecordClass determines the Active Record class to be edited.
+ * A particular record can be edited by specifying the {@link setRecordPk RecordPk}
+ * value (may be an array for composite keys).
+ *
+ * The default editor input controls are created based on the column types.
+ * The editor layout can be specified by a renderer by set the value
+ * of the {@link setEditRenderer EditRenderer} property to the class name of a
+ * class that implements TScaffoldEditRenderer. A renderer is an external
+ * template control that implements IScaffoldEditRenderer.
+ *
+ * The Data of the IScaffoldEditRenderer will be set as the current Active
+ * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer
+ * is called when request to save the record is requested.
+ *
+ * Validators in the custom external editor template should have the
+ * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the
+ * value of the {@link getValidationGroup} of the TScaffoldEditView instance
+ * (the edit view instance is the Parent of the IScaffoldEditRenderer in most
+ * cases.
+ *
+ * Cosmetic changes to the default editor should be done using Cascading Stylesheets.
+ * For example, a particular field/property can be hidden by specifying "display:none" for
+ * the corresponding style (each field/property has unique Css class name as "property_xxx", where
+ * xxx is the property name).
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldEditView extends TScaffoldBase
+{
+ /**
+ * @var IScaffoldEditRenderer custom scaffold edit renderer
+ */
+ private $_editRenderer;
+
+ /**
+ * Initialize the editor form if it is Visible.
+ */
+ public function onLoad($param)
+ {
+ if($this->getVisible())
+ $this->initializeEditForm();
+ }
+
+ /**
+ * @return string the class name for scaffold editor. Defaults to empty, meaning not set.
+ */
+ public function getEditRenderer()
+ {
+ return $this->getViewState('EditRenderer', '');
+ }
+
+ /**
+ * @param string the class name for scaffold editor. Defaults to empty, meaning not set.
+ */
+ public function setEditRenderer($value)
+ {
+ $this->setViewState('EditRenderer', $value, '');
+ }
+
+ /**
+ * @param array Active Record primary key value to be edited.
+ */
+ public function setRecordPk($value)
+ {
+ $this->clearRecordObject();
+ $val = TPropertyValue::ensureArray($value);
+ $this->setViewState('PK', count($val) > 0 ? $val : null);
+ }
+
+ /**
+ * @return array Active Record primary key value.
+ */
+ public function getRecordPk()
+ {
+ return $this->getViewState('PK');
+ }
+
+ /**
+ * @return TActiveRecord current Active Record instance
+ */
+ protected function getCurrentRecord()
+ {
+ return $this->getRecordObject($this->getRecordPk());
+ }
+
+ /**
+ * Initialize the editor form
+ */
+ public function initializeEditForm()
+ {
+ $record = $this->getCurrentRecord();
+ $classPath = $this->getEditRenderer();
+ if($classPath === '')
+ {
+ $columns = $this->getTableInfo()->getColumns();
+ $this->getInputRepeater()->setDataSource($columns);
+ $this->getInputRepeater()->dataBind();
+ }
+ else
+ {
+ if($this->_editRenderer===null)
+ $this->createEditRenderer($record, $classPath);
+ else
+ $this->_editRenderer->setData($record);
+ }
+ }
+
+ /**
+ * Instantiate the external edit renderer.
+ * @param TActiveRecord record to be edited
+ * @param string external edit renderer class name.
+ * @throws TConfigurationException raised when renderer is not an
+ * instance of IScaffoldEditRenderer.
+ */
+ protected function createEditRenderer($record, $classPath)
+ {
+ $this->_editRenderer = Prado::createComponent($classPath);
+ if($this->_editRenderer instanceof IScaffoldEditRenderer)
+ {
+ $index = $this->getControls()->remove($this->getInputRepeater());
+ $this->getControls()->insertAt($index,$this->_editRenderer);
+ $this->_editRenderer->setData($record);
+ }
+ else
+ {
+ throw new TConfigurationException(
+ 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record));
+ }
+ }
+
+ /**
+ * Initialize the default editor using the scaffold input builder.
+ */
+ protected function createRepeaterEditItem($sender, $param)
+ {
+ $type = $param->getItem()->getItemType();
+ if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
+ {
+ $item = $param->getItem();
+ $column = $item->getDataItem();
+ if($column===null)
+ return;
+
+ $record = $this->getCurrentRecord();
+ $builder = $this->getScaffoldInputBuilder($record);
+ $builder->createScaffoldInput($this, $item, $column, $record);
+ }
+ }
+
+ /**
+ * Bubble the command name event. Stops bubbling when the page validator false.
+ * Otherwise, the bubble event is continued.
+ */
+ public function bubbleEvent($sender, $param)
+ {
+ switch(strtolower($param->getCommandName()))
+ {
+ case 'save':
+ return $this->doSave() ? false : true;
+ case 'clear':
+ $this->setRecordPk(null);
+ $this->initializeEditForm();
+ return false;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Check the validators, then tries to save the record.
+ * @return boolean true if the validators are true, false otherwise.
+ */
+ protected function doSave()
+ {
+ if($this->getPage()->getIsValid())
+ {
+ $record = $this->getCurrentRecord();
+ if($this->_editRenderer===null)
+ {
+ $table = $this->getTableInfo();
+ $builder = $this->getScaffoldInputBuilder($record);
+ foreach($this->getInputRepeater()->getItems() as $item)
+ {
+ $column = $table->getColumn($item->getCustomData());
+ $builder->loadScaffoldInput($this, $item, $column, $record);
+ }
+ }
+ else
+ {
+ $this->_editRenderer->updateRecord($record);
+ }
+ $record->save();
+ return true;
+ }
+ else if($this->_editRenderer!==null)
+ {
+ //preserve the form data.
+ $this->_editRenderer->updateRecord($this->getCurrentRecord());
+ }
+
+ return false;
+ }
+
+ /**
+ * @return TRepeater default editor input controls repeater
+ */
+ protected function getInputRepeater()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_repeater');
+ }
+
+ /**
+ * @return TButton Button triggered to save the Active Record.
+ */
+ public function getSaveButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_save');
+ }
+
+ /**
+ * @return TButton Button to clear the editor inputs.
+ */
+ public function getClearButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_clear');
+ }
+
+ /**
+ * @return TButton Button to cancel the edit action (e.g. hide the edit view).
+ */
+ public function getCancelButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_cancel');
+ }
+
+ /**
+ * Create the default scaffold editor control factory.
+ * @param TActiveRecord record instance.
+ * @return TScaffoldInputBase scaffold editor control factory.
+ */
+ protected function getScaffoldInputBuilder($record)
+ {
+ static $_builders=array();
+ $class = get_class($record);
+ if(!isset($_builders[$class]))
+ {
+ Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
+ $_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
+ }
+ return $_builders[$class];
+ }
+
+ /**
+ * @return string editor validation group name.
+ */
+ public function getValidationGroup()
+ {
+ return 'group_'.$this->getUniqueID();
+ }
+}
+
+/**
+ * IScaffoldEditRenderer interface.
+ *
+ * IScaffoldEditRenderer defines the interface that an edit renderer
+ * needs to implement. Besides the {@link getData Data} property, an edit
+ * renderer also needs to provide {@link updateRecord updateRecord} method
+ * that is called before the save() method is called on the TActiveRecord.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+interface IScaffoldEditRenderer extends IDataRenderer
+{
+ /**
+ * This method should update the record with the user input data.
+ * @param TActiveRecord record to be saved.
+ */
+ public function updateRecord($record);
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
index 953420e3..bed5bf88 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
@@ -1,306 +1,306 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load the scaffold base class.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldListView displays a list of Active Records.
- *
- * The {@link getHeader Header} property is a TRepeater displaying the
- * Active Record property/field names. The {@link getSort Sort} property
- * is a drop down list displaying the combination of properties and its possible
- * ordering. The {@link getPager Pager} property is a TPager control displaying
- * the links and/or buttons that navigate to different pages in the Active Record data.
- * The {@link getList List} property is a TRepeater that renders a row of
- * Active Record data.
- *
- * Custom rendering of the each Active Record can be achieved by specifying
- * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List}
- * repeater.
- *
- * The TScaffoldListView will listen for two command events named "delete" and
- * "edit". A "delete" command will delete a the record for the row where the
- * "delete" command is originates. An "edit" command will push
- * the record data to be edited by a TScaffoldEditView with ID specified by the
- * {@link setEditViewID EditViewID}.
- *
- * Additional {@link setSearchCondition SearchCondition} and
- * {@link setSearchParameters SearchParameters} (takes array values) can be
- * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition}
- * will be used as the Condition property of TActiveRecordCriteria, and similarly
- * the {@link setSearchParameters SearchParameters} will be the corresponding
- * Parameters property of TActiveRecordCriteria.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldListView extends TScaffoldBase
-{
- /**
- * Initialize the sort drop down list and the column names repeater.
- */
- protected function initializeSort()
- {
- $table = $this->getTableInfo();
- $sorts = array('Sort By', str_repeat('-',15));
- $headers = array();
- foreach($table->getColumns() as $name=>$colum)
- {
- $fname = ucwords(str_replace('_', ' ', $name));
- $sorts[$name.' ASC'] = $fname .' Ascending';
- $sorts[$name.' DESC'] = $fname .' Descending';
- $headers[] = $fname ;
- }
- $this->_sort->setDataSource($sorts);
- $this->_sort->dataBind();
- $this->_header->setDataSource($headers);
- $this->_header->dataBind();
- }
-
- /**
- * Loads and display the data.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass())
- {
- $this->initializeSort();
- $this->setViewState('CurrentClass', $this->getRecordClass());
- }
- $this->loadRecordData();
- }
-
- /**
- * Fetch the records and data bind it to the list.
- */
- protected function loadRecordData()
- {
- $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
- $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search));
- $finder = $this->getRecordFinder();
- $criteria = $this->getRecordCriteria();
- $this->_list->setDataSource($finder->findAll($criteria));
- $this->_list->dataBind();
- }
-
- /**
- * @return TActiveRecordCriteria sort/search/paging criteria
- */
- protected function getRecordCriteria()
- {
- $total = $this->_list->getVirtualItemCount();
- $limit = $this->_list->getPageSize();
- $offset = $this->_list->getCurrentPageIndex()*$limit;
- if($offset + $limit > $total)
- $limit = $total - $offset;
- $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
- if($limit > 0)
- {
- $criteria->setLimit($limit);
- if($offset <= $total)
- $criteria->setOffset($offset);
- }
- $order = explode(' ',$this->_sort->getSelectedValue(), 2);
- if(is_array($order) && count($order) === 2)
- $criteria->OrdersBy[$order[0]] = $order[1];
- return $criteria;
- }
-
- /**
- * @param string search condition, the SQL string after the WHERE clause.
- */
- public function setSearchCondition($value)
- {
- $this->setViewState('SearchCondition', $value);
- }
-
- /**
- * @param string SQL search condition for list display.
- */
- public function getSearchCondition()
- {
- return $this->getViewState('SearchCondition');
- }
-
- /**
- * @param array search parameters
- */
- public function setSearchParameters($value)
- {
- $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array());
- }
-
- /**
- * @return array search parameters
- */
- public function getSearchParameters()
- {
- return $this->getViewState('SearchParameters', array());
- }
-
- /**
- * Continue bubbling the "edit" command, "delete" command is handled in this class.
- */
- public function bubbleEvent($sender, $param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'delete':
- return $this->deleteRecord($sender, $param);
- case 'edit':
- $this->initializeEdit($sender, $param);
- }
- $this->raiseBubbleEvent($this, $param);
- return true;
- }
-
- /**
- * Initialize the edit view control form when EditViewID is set.
- */
- protected function initializeEdit($sender, $param)
- {
- if(($ctrl=$this->getEditViewControl())!==null)
- {
- if($param instanceof TRepeaterCommandEventParameter)
- {
- $pk = $param->getItem()->getCustomData();
- $ctrl->setRecordPk($pk);
- $ctrl->initializeEditForm();
- }
- }
- }
-
- /**
- * Deletes an Active Record.
- */
- protected function deleteRecord($sender, $param)
- {
- if($param instanceof TRepeaterCommandEventParameter)
- {
- $pk = $param->getItem()->getCustomData();
- $this->getRecordFinder()->deleteByPk($pk);
- }
- }
-
- /**
- * Initialize the default display for each Active Record item.
- */
- protected function listItemCreated($sender, $param)
- {
- $item = $param->getItem();
- if($item instanceof IItemDataRenderer)
- {
- $type = $item->getItemType();
- if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
- $this->populateField($sender, $param);
- }
- }
-
- /**
- * Sets the Record primary key to the current repeater item's CustomData.
- * Binds the inner repeater with properties of the current Active Record.
- */
- protected function populateField($sender, $param)
- {
- $item = $param->getItem();
- if(($data = $item->getData()) !== null)
- {
- $item->setCustomData($this->getRecordPkValues($data));
- if(($prop = $item->findControl('_properties'))!==null)
- {
- $item->_properties->setDataSource($this->getRecordPropertyValues($data));
- $item->_properties->dataBind();
- }
- }
- }
-
- /**
- * Updates repeater page index with the pager new index value.
- */
- protected function pageChanged($sender, $param)
- {
- $this->_list->setCurrentPageIndex($param->getNewPageIndex());
- }
-
- /**
- * @return TRepeater Repeater control for Active Record instances.
- */
- public function getList()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_list');
- }
-
- /**
- * @return TPager List pager control.
- */
- public function getPager()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_pager');
- }
-
- /**
- * @return TDropDownList Control that displays and controls the record ordering.
- */
- public function getSort()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_sort');
- }
-
- /**
- * @return TRepeater Repeater control for record property names.
- */
- public function getHeader()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_header');
- }
-
- /**
- * @return string TScaffoldEditView control ID for editing selected Active Record.
- */
- public function getEditViewID()
- {
- return $this->getViewState('EditViewID');
- }
-
- /**
- * @param string TScaffoldEditView control ID for editing selected Active Record.
- */
- public function setEditViewID($value)
- {
- $this->setViewState('EditViewID', $value);
- }
-
- /**
- * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set.
- */
- protected function getEditViewControl()
- {
- if(($id=$this->getEditViewID())!==null)
- {
- $ctrl = $this->getParent()->findControl($id);
- if($ctrl===null)
- throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
- return $ctrl;
- }
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Load the scaffold base class.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * TScaffoldListView displays a list of Active Records.
+ *
+ * The {@link getHeader Header} property is a TRepeater displaying the
+ * Active Record property/field names. The {@link getSort Sort} property
+ * is a drop down list displaying the combination of properties and its possible
+ * ordering. The {@link getPager Pager} property is a TPager control displaying
+ * the links and/or buttons that navigate to different pages in the Active Record data.
+ * The {@link getList List} property is a TRepeater that renders a row of
+ * Active Record data.
+ *
+ * Custom rendering of the each Active Record can be achieved by specifying
+ * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List}
+ * repeater.
+ *
+ * The TScaffoldListView will listen for two command events named "delete" and
+ * "edit". A "delete" command will delete a the record for the row where the
+ * "delete" command is originates. An "edit" command will push
+ * the record data to be edited by a TScaffoldEditView with ID specified by the
+ * {@link setEditViewID EditViewID}.
+ *
+ * Additional {@link setSearchCondition SearchCondition} and
+ * {@link setSearchParameters SearchParameters} (takes array values) can be
+ * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition}
+ * will be used as the Condition property of TActiveRecordCriteria, and similarly
+ * the {@link setSearchParameters SearchParameters} will be the corresponding
+ * Parameters property of TActiveRecordCriteria.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldListView extends TScaffoldBase
+{
+ /**
+ * Initialize the sort drop down list and the column names repeater.
+ */
+ protected function initializeSort()
+ {
+ $table = $this->getTableInfo();
+ $sorts = array('Sort By', str_repeat('-',15));
+ $headers = array();
+ foreach($table->getColumns() as $name=>$colum)
+ {
+ $fname = ucwords(str_replace('_', ' ', $name));
+ $sorts[$name.' ASC'] = $fname .' Ascending';
+ $sorts[$name.' DESC'] = $fname .' Descending';
+ $headers[] = $fname ;
+ }
+ $this->_sort->setDataSource($sorts);
+ $this->_sort->dataBind();
+ $this->_header->setDataSource($headers);
+ $this->_header->dataBind();
+ }
+
+ /**
+ * Loads and display the data.
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass())
+ {
+ $this->initializeSort();
+ $this->setViewState('CurrentClass', $this->getRecordClass());
+ }
+ $this->loadRecordData();
+ }
+
+ /**
+ * Fetch the records and data bind it to the list.
+ */
+ protected function loadRecordData()
+ {
+ $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
+ $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search));
+ $finder = $this->getRecordFinder();
+ $criteria = $this->getRecordCriteria();
+ $this->_list->setDataSource($finder->findAll($criteria));
+ $this->_list->dataBind();
+ }
+
+ /**
+ * @return TActiveRecordCriteria sort/search/paging criteria
+ */
+ protected function getRecordCriteria()
+ {
+ $total = $this->_list->getVirtualItemCount();
+ $limit = $this->_list->getPageSize();
+ $offset = $this->_list->getCurrentPageIndex()*$limit;
+ if($offset + $limit > $total)
+ $limit = $total - $offset;
+ $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
+ if($limit > 0)
+ {
+ $criteria->setLimit($limit);
+ if($offset <= $total)
+ $criteria->setOffset($offset);
+ }
+ $order = explode(' ',$this->_sort->getSelectedValue(), 2);
+ if(is_array($order) && count($order) === 2)
+ $criteria->OrdersBy[$order[0]] = $order[1];
+ return $criteria;
+ }
+
+ /**
+ * @param string search condition, the SQL string after the WHERE clause.
+ */
+ public function setSearchCondition($value)
+ {
+ $this->setViewState('SearchCondition', $value);
+ }
+
+ /**
+ * @param string SQL search condition for list display.
+ */
+ public function getSearchCondition()
+ {
+ return $this->getViewState('SearchCondition');
+ }
+
+ /**
+ * @param array search parameters
+ */
+ public function setSearchParameters($value)
+ {
+ $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array());
+ }
+
+ /**
+ * @return array search parameters
+ */
+ public function getSearchParameters()
+ {
+ return $this->getViewState('SearchParameters', array());
+ }
+
+ /**
+ * Continue bubbling the "edit" command, "delete" command is handled in this class.
+ */
+ public function bubbleEvent($sender, $param)
+ {
+ switch(strtolower($param->getCommandName()))
+ {
+ case 'delete':
+ return $this->deleteRecord($sender, $param);
+ case 'edit':
+ $this->initializeEdit($sender, $param);
+ }
+ $this->raiseBubbleEvent($this, $param);
+ return true;
+ }
+
+ /**
+ * Initialize the edit view control form when EditViewID is set.
+ */
+ protected function initializeEdit($sender, $param)
+ {
+ if(($ctrl=$this->getEditViewControl())!==null)
+ {
+ if($param instanceof TRepeaterCommandEventParameter)
+ {
+ $pk = $param->getItem()->getCustomData();
+ $ctrl->setRecordPk($pk);
+ $ctrl->initializeEditForm();
+ }
+ }
+ }
+
+ /**
+ * Deletes an Active Record.
+ */
+ protected function deleteRecord($sender, $param)
+ {
+ if($param instanceof TRepeaterCommandEventParameter)
+ {
+ $pk = $param->getItem()->getCustomData();
+ $this->getRecordFinder()->deleteByPk($pk);
+ }
+ }
+
+ /**
+ * Initialize the default display for each Active Record item.
+ */
+ protected function listItemCreated($sender, $param)
+ {
+ $item = $param->getItem();
+ if($item instanceof IItemDataRenderer)
+ {
+ $type = $item->getItemType();
+ if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
+ $this->populateField($sender, $param);
+ }
+ }
+
+ /**
+ * Sets the Record primary key to the current repeater item's CustomData.
+ * Binds the inner repeater with properties of the current Active Record.
+ */
+ protected function populateField($sender, $param)
+ {
+ $item = $param->getItem();
+ if(($data = $item->getData()) !== null)
+ {
+ $item->setCustomData($this->getRecordPkValues($data));
+ if(($prop = $item->findControl('_properties'))!==null)
+ {
+ $item->_properties->setDataSource($this->getRecordPropertyValues($data));
+ $item->_properties->dataBind();
+ }
+ }
+ }
+
+ /**
+ * Updates repeater page index with the pager new index value.
+ */
+ protected function pageChanged($sender, $param)
+ {
+ $this->_list->setCurrentPageIndex($param->getNewPageIndex());
+ }
+
+ /**
+ * @return TRepeater Repeater control for Active Record instances.
+ */
+ public function getList()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_list');
+ }
+
+ /**
+ * @return TPager List pager control.
+ */
+ public function getPager()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_pager');
+ }
+
+ /**
+ * @return TDropDownList Control that displays and controls the record ordering.
+ */
+ public function getSort()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_sort');
+ }
+
+ /**
+ * @return TRepeater Repeater control for record property names.
+ */
+ public function getHeader()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_header');
+ }
+
+ /**
+ * @return string TScaffoldEditView control ID for editing selected Active Record.
+ */
+ public function getEditViewID()
+ {
+ return $this->getViewState('EditViewID');
+ }
+
+ /**
+ * @param string TScaffoldEditView control ID for editing selected Active Record.
+ */
+ public function setEditViewID($value)
+ {
+ $this->setViewState('EditViewID', $value);
+ }
+
+ /**
+ * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set.
+ */
+ protected function getEditViewControl()
+ {
+ if(($id=$this->getEditViewID())!==null)
+ {
+ $ctrl = $this->getParent()->findControl($id);
+ if($ctrl===null)
+ throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
+ return $ctrl;
+ }
+ }
+}
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
index bd679c94..40335085 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
@@ -1,150 +1,150 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import the scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldSearch provide a simple textbox and a button that is used
- * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}.
- *
- * The {@link getSearchText SearchText} property is a TTextBox and the
- * {@link getSearchButton SearchButton} property is a TButton with label value "Search".
- *
- * Searchable fields of the Active Record can be restricted by specifying
- * a comma delimited string of allowable fields in the
- * {@link setSearchableFields SearchableFields} property. The default is null,
- * meaning that most text type fields are searched (the default searchable fields
- * are database dependent).
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldSearch extends TScaffoldBase
-{
- /**
- * @var TScaffoldListView the scaffold list view.
- */
- private $_list;
-
- /**
- * @return TScaffoldListView the scaffold list view this search box belongs to.
- */
- protected function getListView()
- {
- if($this->_list===null && ($id = $this->getListViewID()) !== null)
- {
- $this->_list = $this->getParent()->findControl($id);
- if($this->_list ===null)
- throw new TConfigurationException('scaffold_unable_to_find_list_view', $id);
- }
- return $this->_list;
- }
-
- /**
- * @param string ID of the TScaffoldListView this search control belongs to.
- */
- public function setListViewID($value)
- {
- $this->setViewState('ListViewID', $value);
- }
-
- /**
- * @return string ID of the TScaffoldListView this search control belongs to.
- */
- public function getListViewID()
- {
- return $this->getViewState('ListViewID');
- }
-
- /**
- * Sets the SearchCondition of the TScaffoldListView as the search terms
- * given by the text of the search text box.
- */
- public function bubbleEvent($sender, $param)
- {
- if(strtolower($param->getCommandName())==='search')
- {
- if(($list = $this->getListView()) !== null)
- {
- $list->setSearchCondition($this->createSearchCondition());
- return false;
- }
- }
- $this->raiseBubbleEvent($this, $param);
- return true;
- }
-
- /**
- * @return string the search criteria for the search terms in the search text box.
- */
- protected function createSearchCondition()
- {
- $table = $this->getTableInfo();
- if(strlen($str=$this->getSearchText()->getText()) > 0)
- {
- $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection());
- return $builder->getSearchExpression($this->getFields(), $str);
- }
- }
-
- /**
- * @return array list of fields to be searched.
- */
- protected function getFields()
- {
- if(strlen(trim($str=$this->getSearchableFields()))>0)
- $fields = preg_split('/\s*,\s*/', $str);
- else
- $fields = $this->getTableInfo()->getColumns()->getKeys();
- return $fields;
- }
-
- /**
- * @return string comma delimited list of fields that may be searched.
- */
- public function getSearchableFields()
- {
- return $this->getViewState('SearchableFields','');
- }
-
- /**
- * @param string comma delimited list of fields that may be searched.
- */
- public function setSearchableFields($value)
- {
- $this->setViewState('SearchableFields', $value, '');
- }
-
- /**
- * @return TButton button with default label "Search".
- */
- public function getSearchButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_search');
- }
-
- /**
- * @return TTextBox search text box.
- */
- public function getSearchText()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_textbox');
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Import the scaffold base.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * TScaffoldSearch provide a simple textbox and a button that is used
+ * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}.
+ *
+ * The {@link getSearchText SearchText} property is a TTextBox and the
+ * {@link getSearchButton SearchButton} property is a TButton with label value "Search".
+ *
+ * Searchable fields of the Active Record can be restricted by specifying
+ * a comma delimited string of allowable fields in the
+ * {@link setSearchableFields SearchableFields} property. The default is null,
+ * meaning that most text type fields are searched (the default searchable fields
+ * are database dependent).
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldSearch extends TScaffoldBase
+{
+ /**
+ * @var TScaffoldListView the scaffold list view.
+ */
+ private $_list;
+
+ /**
+ * @return TScaffoldListView the scaffold list view this search box belongs to.
+ */
+ protected function getListView()
+ {
+ if($this->_list===null && ($id = $this->getListViewID()) !== null)
+ {
+ $this->_list = $this->getParent()->findControl($id);
+ if($this->_list ===null)
+ throw new TConfigurationException('scaffold_unable_to_find_list_view', $id);
+ }
+ return $this->_list;
+ }
+
+ /**
+ * @param string ID of the TScaffoldListView this search control belongs to.
+ */
+ public function setListViewID($value)
+ {
+ $this->setViewState('ListViewID', $value);
+ }
+
+ /**
+ * @return string ID of the TScaffoldListView this search control belongs to.
+ */
+ public function getListViewID()
+ {
+ return $this->getViewState('ListViewID');
+ }
+
+ /**
+ * Sets the SearchCondition of the TScaffoldListView as the search terms
+ * given by the text of the search text box.
+ */
+ public function bubbleEvent($sender, $param)
+ {
+ if(strtolower($param->getCommandName())==='search')
+ {
+ if(($list = $this->getListView()) !== null)
+ {
+ $list->setSearchCondition($this->createSearchCondition());
+ return false;
+ }
+ }
+ $this->raiseBubbleEvent($this, $param);
+ return true;
+ }
+
+ /**
+ * @return string the search criteria for the search terms in the search text box.
+ */
+ protected function createSearchCondition()
+ {
+ $table = $this->getTableInfo();
+ if(strlen($str=$this->getSearchText()->getText()) > 0)
+ {
+ $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection());
+ return $builder->getSearchExpression($this->getFields(), $str);
+ }
+ }
+
+ /**
+ * @return array list of fields to be searched.
+ */
+ protected function getFields()
+ {
+ if(strlen(trim($str=$this->getSearchableFields()))>0)
+ $fields = preg_split('/\s*,\s*/', $str);
+ else
+ $fields = $this->getTableInfo()->getColumns()->getKeys();
+ return $fields;
+ }
+
+ /**
+ * @return string comma delimited list of fields that may be searched.
+ */
+ public function getSearchableFields()
+ {
+ return $this->getViewState('SearchableFields','');
+ }
+
+ /**
+ * @param string comma delimited list of fields that may be searched.
+ */
+ public function setSearchableFields($value)
+ {
+ $this->setViewState('SearchableFields', $value, '');
+ }
+
+ /**
+ * @return TButton button with default label "Search".
+ */
+ public function getSearchButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_search');
+ }
+
+ /**
+ * @return TTextBox search text box.
+ */
+ public function getSearchText()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_textbox');
+ }
+}
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
index 69bc1f81..40eee21d 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
@@ -1,143 +1,143 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import scaffold base, list, edit and search controls.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch');
-
-/**
- * TScaffoldView is a composite control consisting of TScaffoldListView
- * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView
- * when an "edit" command is raised from the TScaffoldListView (when the
- * edit button is clicked). Futher more, the "add" button can be clicked
- * that shows an empty data TScaffoldListView for creating new records.
- *
- * The {@link getListView ListView} property gives a TScaffoldListView for
- * display the record data. The {@link getEditView EditView} is the
- * TScaffoldEditView that renders the
- * inputs for editing and adding records. The {@link getSearchControl SearchControl}
- * is a TScaffoldSearch responsible to the search user interface.
- *
- * Set the {@link setRecordClass RecordClass} property to the name of
- * the Active Record class to be displayed/edited/added.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.0
- */
-class TScaffoldView extends TScaffoldBase
-{
- /**
- * Copy basic record details to the list/edit/search controls.
- */
- public function onPreRender($param)
- {
- parent::onPreRender($param);
- $this->getListView()->copyFrom($this);
- $this->getEditView()->copyFrom($this);
- $this->getSearchControl()->copyFrom($this);
- }
-
- /**
- * @return TScaffoldListView scaffold list view.
- */
- public function getListView()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_listView');
- }
-
- /**
- * @return TScaffoldEditView scaffold edit view.
- */
- public function getEditView()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_editView');
- }
-
- /**
- * @return TScaffoldSearch scaffold search textbox and button.
- */
- public function getSearchControl()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_search');
- }
-
- /**
- * @return TButton "Add new record" button.
- */
- public function getAddButton()
- {
- $this->ensureChildControls();
- return $this->getRegisteredObject('_newButton');
- }
-
- /**
- * Handle the "edit" and "new" commands by displaying the edit view.
- * Default command shows the list view.
- */
- public function bubbleEvent($sender,$param)
- {
- switch(strtolower($param->getCommandName()))
- {
- case 'edit':
- return $this->showEditView($sender, $param);
- case 'new':
- return $this->showAddView($sender, $param);
- default:
- return $this->showListView($sender, $param);
- }
- return false;
- }
-
- /**
- * Shows the edit record view.
- */
- protected function showEditView($sender, $param)
- {
- $this->getListView()->setVisible(false);
- $this->getEditView()->setVisible(true);
- $this->_panForNewButton->setVisible(false);
- $this->_panForSearch->setVisible(false);
- $this->getEditView()->getCancelButton()->setVisible(true);
- $this->getEditView()->getClearButton()->setVisible(false);
- }
-
- /**
- * Shows the view for listing the records.
- */
- protected function showListView($sender, $param)
- {
- $this->getListView()->setVisible(true);
- $this->getEditView()->setVisible(false);
- $this->_panForNewButton->setVisible(true);
- $this->_panForSearch->setVisible(true);
- }
-
- /**
- * Shows the add record view.
- */
- protected function showAddView($sender, $param)
- {
- $this->getEditView()->setRecordPk(null);
- $this->getEditView()->initializeEditForm();
- $this->showEditView($sender, $param);
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Import scaffold base, list, edit and search controls.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch');
+
+/**
+ * TScaffoldView is a composite control consisting of TScaffoldListView
+ * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView
+ * when an "edit" command is raised from the TScaffoldListView (when the
+ * edit button is clicked). Futher more, the "add" button can be clicked
+ * that shows an empty data TScaffoldListView for creating new records.
+ *
+ * The {@link getListView ListView} property gives a TScaffoldListView for
+ * display the record data. The {@link getEditView EditView} is the
+ * TScaffoldEditView that renders the
+ * inputs for editing and adding records. The {@link getSearchControl SearchControl}
+ * is a TScaffoldSearch responsible to the search user interface.
+ *
+ * Set the {@link setRecordClass RecordClass} property to the name of
+ * the Active Record class to be displayed/edited/added.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.0
+ */
+class TScaffoldView extends TScaffoldBase
+{
+ /**
+ * Copy basic record details to the list/edit/search controls.
+ */
+ public function onPreRender($param)
+ {
+ parent::onPreRender($param);
+ $this->getListView()->copyFrom($this);
+ $this->getEditView()->copyFrom($this);
+ $this->getSearchControl()->copyFrom($this);
+ }
+
+ /**
+ * @return TScaffoldListView scaffold list view.
+ */
+ public function getListView()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_listView');
+ }
+
+ /**
+ * @return TScaffoldEditView scaffold edit view.
+ */
+ public function getEditView()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_editView');
+ }
+
+ /**
+ * @return TScaffoldSearch scaffold search textbox and button.
+ */
+ public function getSearchControl()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_search');
+ }
+
+ /**
+ * @return TButton "Add new record" button.
+ */
+ public function getAddButton()
+ {
+ $this->ensureChildControls();
+ return $this->getRegisteredObject('_newButton');
+ }
+
+ /**
+ * Handle the "edit" and "new" commands by displaying the edit view.
+ * Default command shows the list view.
+ */
+ public function bubbleEvent($sender,$param)
+ {
+ switch(strtolower($param->getCommandName()))
+ {
+ case 'edit':
+ return $this->showEditView($sender, $param);
+ case 'new':
+ return $this->showAddView($sender, $param);
+ default:
+ return $this->showListView($sender, $param);
+ }
+ return false;
+ }
+
+ /**
+ * Shows the edit record view.
+ */
+ protected function showEditView($sender, $param)
+ {
+ $this->getListView()->setVisible(false);
+ $this->getEditView()->setVisible(true);
+ $this->_panForNewButton->setVisible(false);
+ $this->_panForSearch->setVisible(false);
+ $this->getEditView()->getCancelButton()->setVisible(true);
+ $this->getEditView()->getClearButton()->setVisible(false);
+ }
+
+ /**
+ * Shows the view for listing the records.
+ */
+ protected function showListView($sender, $param)
+ {
+ $this->getListView()->setVisible(true);
+ $this->getEditView()->setVisible(false);
+ $this->_panForNewButton->setVisible(true);
+ $this->_panForSearch->setVisible(true);
+ }
+
+ /**
+ * Shows the add record view.
+ */
+ protected function showAddView($sender, $param)
+ {
+ $this->getEditView()->setRecordPk(null);
+ $this->getEditView()->initializeEditForm();
+ $this->showEditView($sender, $param);
+ }
+}
+
diff --git a/framework/Data/ActiveRecord/TActiveRecordConfig.php b/framework/Data/ActiveRecord/TActiveRecordConfig.php
index 478786d3..821c4223 100644
--- a/framework/Data/ActiveRecord/TActiveRecordConfig.php
+++ b/framework/Data/ActiveRecord/TActiveRecordConfig.php
@@ -1,201 +1,201 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDataSourceConfig');
-Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
-
-/**
- * TActiveRecordConfig module configuration class.
- *
- * Database configuration for the default ActiveRecord manager instance.
- *
- * Example: application.xml configuration
- *
- *
- *
- *
- *
- *
- *
- *
- * MySQL database definition:
- *
- * CREATE TABLE `blogs` (
- * `blog_id` int(10) unsigned NOT NULL auto_increment,
- * `blog_name` varchar(255) NOT NULL,
- * `blog_author` varchar(255) NOT NULL,
- * PRIMARY KEY (`blog_id`)
- * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- *
- *
- * Record php class:
- *
- * class Blogs extends TActiveRecord
- * {
- * public $blog_id;
- * public $blog_name;
- * public $blog_author;
- *
- * public static function finder($className=__CLASS__)
- * {
- * return parent::finder($className);
- * }
- * }
- *
- *
- * Usage example:
- *
- * class Home extends TPage
- * {
- * function onLoad($param)
- * {
- * $blogs = Blogs::finder()->findAll();
- * print_r($blogs);
- * }
- * }
- *
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordConfig extends TDataSourceConfig
-{
- const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager';
- const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
- /**
- * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
- * @var string
- */
- private $_managerClass = self::DEFAULT_MANAGER_CLASS;
-
- /**
- * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- * @var string
- */
- private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
- /**
- * @var TActiveRecordManager
- */
- private $_manager = null;
-
- private $_enableCache=false;
-
- /**
- * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
- *
- * @var TActiveRecordInvalidFinderResult
- * @since 3.1.5
- */
- private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
- /**
- * Initialize the active record manager.
- * @param TXmlDocument xml configuration.
- */
- public function init($xml)
- {
- parent::init($xml);
- $manager = $this -> getManager();
- if($this->getEnableCache())
- $manager->setCache($this->getApplication()->getCache());
- $manager->setDbConnection($this->getDbConnection());
- $manager->setInvalidFinderResult($this->getInvalidFinderResult());
- $manager->setGatewayClass($this->getGatewayClass());
- }
-
- /**
- * @return TActiveRecordManager
- */
- public function getManager() {
- if($this->_manager === null)
- $this->_manager = Prado::createComponent($this -> getManagerClass());
- return TActiveRecordManager::getInstance($this->_manager);
- }
-
- /**
- * Set implementation class of ActiveRecordManager
- * @param string $value
- */
- public function setManagerClass($value)
- {
- $this->_managerClass = TPropertyValue::ensureString($value);
- }
-
- /**
- * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
- */
- public function getManagerClass()
- {
- return $this->_managerClass;
- }
-
- /**
- * Set implementation class of ActiveRecordGateway
- * @param string $value
- */
- public function setGatewayClass($value)
- {
- $this->_gatewayClass = TPropertyValue::ensureString($value);
- }
-
- /**
- * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- */
- public function getGatewayClass()
- {
- return $this->_gatewayClass;
- }
-
- /**
- * Set true to cache the table meta data.
- * @param boolean true to cache sqlmap instance.
- */
- public function setEnableCache($value)
- {
- $this->_enableCache = TPropertyValue::ensureBoolean($value);
- }
-
- /**
- * @return boolean true if table meta data should be cached, false otherwise.
- */
- public function getEnableCache()
- {
- return $this->_enableCache;
- }
-
- /**
- * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
- * @see setInvalidFinderResult
- * @since 3.1.5
- */
- public function getInvalidFinderResult()
- {
- return $this->_invalidFinderResult;
- }
-
- /**
- * Define the way an active record finder react if an invalid magic-finder invoked
- *
- * @param TActiveRecordInvalidFinderResult
- * @see getInvalidFinderResult
- * @since 3.1.5
- */
- public function setInvalidFinderResult($value)
- {
- $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.TDataSourceConfig');
+Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
+
+/**
+ * TActiveRecordConfig module configuration class.
+ *
+ * Database configuration for the default ActiveRecord manager instance.
+ *
+ * Example: application.xml configuration
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * MySQL database definition:
+ *
+ * CREATE TABLE `blogs` (
+ * `blog_id` int(10) unsigned NOT NULL auto_increment,
+ * `blog_name` varchar(255) NOT NULL,
+ * `blog_author` varchar(255) NOT NULL,
+ * PRIMARY KEY (`blog_id`)
+ * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ *
+ *
+ * Record php class:
+ *
+ * class Blogs extends TActiveRecord
+ * {
+ * public $blog_id;
+ * public $blog_name;
+ * public $blog_author;
+ *
+ * public static function finder($className=__CLASS__)
+ * {
+ * return parent::finder($className);
+ * }
+ * }
+ *
+ *
+ * Usage example:
+ *
+ * class Home extends TPage
+ * {
+ * function onLoad($param)
+ * {
+ * $blogs = Blogs::finder()->findAll();
+ * print_r($blogs);
+ * }
+ * }
+ *
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordConfig extends TDataSourceConfig
+{
+ const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager';
+ const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
+
+ /**
+ * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
+ * @var string
+ */
+ private $_managerClass = self::DEFAULT_MANAGER_CLASS;
+
+ /**
+ * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+ * @var string
+ */
+ private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
+
+ /**
+ * @var TActiveRecordManager
+ */
+ private $_manager = null;
+
+ private $_enableCache=false;
+
+ /**
+ * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+ *
+ * @var TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ */
+ private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+ /**
+ * Initialize the active record manager.
+ * @param TXmlDocument xml configuration.
+ */
+ public function init($xml)
+ {
+ parent::init($xml);
+ $manager = $this -> getManager();
+ if($this->getEnableCache())
+ $manager->setCache($this->getApplication()->getCache());
+ $manager->setDbConnection($this->getDbConnection());
+ $manager->setInvalidFinderResult($this->getInvalidFinderResult());
+ $manager->setGatewayClass($this->getGatewayClass());
+ }
+
+ /**
+ * @return TActiveRecordManager
+ */
+ public function getManager() {
+ if($this->_manager === null)
+ $this->_manager = Prado::createComponent($this -> getManagerClass());
+ return TActiveRecordManager::getInstance($this->_manager);
+ }
+
+ /**
+ * Set implementation class of ActiveRecordManager
+ * @param string $value
+ */
+ public function setManagerClass($value)
+ {
+ $this->_managerClass = TPropertyValue::ensureString($value);
+ }
+
+ /**
+ * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
+ */
+ public function getManagerClass()
+ {
+ return $this->_managerClass;
+ }
+
+ /**
+ * Set implementation class of ActiveRecordGateway
+ * @param string $value
+ */
+ public function setGatewayClass($value)
+ {
+ $this->_gatewayClass = TPropertyValue::ensureString($value);
+ }
+
+ /**
+ * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+ */
+ public function getGatewayClass()
+ {
+ return $this->_gatewayClass;
+ }
+
+ /**
+ * Set true to cache the table meta data.
+ * @param boolean true to cache sqlmap instance.
+ */
+ public function setEnableCache($value)
+ {
+ $this->_enableCache = TPropertyValue::ensureBoolean($value);
+ }
+
+ /**
+ * @return boolean true if table meta data should be cached, false otherwise.
+ */
+ public function getEnableCache()
+ {
+ return $this->_enableCache;
+ }
+
+ /**
+ * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+ * @see setInvalidFinderResult
+ * @since 3.1.5
+ */
+ public function getInvalidFinderResult()
+ {
+ return $this->_invalidFinderResult;
+ }
+
+ /**
+ * Define the way an active record finder react if an invalid magic-finder invoked
+ *
+ * @param TActiveRecordInvalidFinderResult
+ * @see getInvalidFinderResult
+ * @since 3.1.5
+ */
+ public function setInvalidFinderResult($value)
+ {
+ $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+ }
+}
diff --git a/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
index d15f83ee..cdd7f964 100644
--- a/framework/Data/ActiveRecord/TActiveRecordCriteria.php
+++ b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
@@ -1,39 +1,39 @@
- * @link http://www.pradosoft.com/
+/**
+ * TActiveRecordCriteria class file.
+ *
+ * @author Wei Zhuo
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.DataGateway.TSqlCriteria');
-
-/**
- * Search criteria for Active Record.
- *
- * Criteria object for active record finder methods. Usage:
- *
- * $criteria = new TActiveRecordCriteria;
- * $criteria->Condition = 'username = :name AND password = :pass';
- * $criteria->Parameters[':name'] = 'admin';
- * $criteria->Parameters[':pass'] = 'prado';
- * $criteria->OrdersBy['level'] = 'desc';
- * $criteria->OrdersBy['name'] = 'asc';
- * $criteria->Limit = 10;
- * $criteria->Offset = 20;
- *
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordCriteria extends TSqlCriteria
-{
-
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.DataGateway.TSqlCriteria');
+
+/**
+ * Search criteria for Active Record.
+ *
+ * Criteria object for active record finder methods. Usage:
+ *
+ * $criteria = new TActiveRecordCriteria;
+ * $criteria->Condition = 'username = :name AND password = :pass';
+ * $criteria->Parameters[':name'] = 'admin';
+ * $criteria->Parameters[':pass'] = 'prado';
+ * $criteria->OrdersBy['level'] = 'desc';
+ * $criteria->OrdersBy['name'] = 'asc';
+ * $criteria->Limit = 10;
+ * $criteria->Offset = 20;
+ *
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordCriteria extends TSqlCriteria
+{
+
+}
diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php
index 00979d1c..6cca76e7 100644
--- a/framework/Data/ActiveRecord/TActiveRecordManager.php
+++ b/framework/Data/ActiveRecord/TActiveRecordManager.php
@@ -1,163 +1,163 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDbConnection');
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException');
-Prado::using('System.Data.ActiveRecord.TActiveRecordGateway');
-
-/**
- * TActiveRecordManager provides the default DB connection,
- * default active record gateway, and table meta data inspector.
- *
- * The default connection can be set as follows:
- *
- * TActiveRecordManager::getInstance()->setDbConnection($conn);
- *
- * All new active record created after setting the
- * {@link DbConnection setDbConnection()} will use that connection unless
- * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection().
- *
- * Set the {@link setCache Cache} property to an ICache object to allow
- * the active record gateway to cache the table meta data information.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordManager extends TComponent
-{
- const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
- /**
- * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- * @var string
- */
- private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
- private $_gateway;
- private $_meta=array();
- private $_connection;
-
- private $_cache;
-
- /**
- * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
- *
- * @var TActiveRecordInvalidFinderResult
- * @since 3.1.5
- */
- private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
- /**
- * @return ICache application cache.
- */
- public function getCache()
- {
- return $this->_cache;
- }
-
- /**
- * @param ICache application cache
- */
- public function setCache($value)
- {
- $this->_cache=$value;
- }
-
- /**
- * @param TDbConnection default database connection
- */
- public function setDbConnection($conn)
- {
- $this->_connection=$conn;
- }
-
- /**
- * @return TDbConnection default database connection
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @return TActiveRecordManager static instance of record manager.
- */
- public static function getInstance($self=null)
- {
- static $instance;
- if($self!==null)
- $instance=$self;
- else if($instance===null)
- $instance = new self;
- return $instance;
- }
-
- /**
- * @return TActiveRecordGateway record gateway.
- */
- public function getRecordGateway()
- {
- if($this->_gateway === null) {
- $this->_gateway = $this->createRecordGateway();
- }
- return $this->_gateway;
- }
-
- /**
- * @return TActiveRecordGateway default record gateway.
- */
- protected function createRecordGateway()
- {
- return Prado::createComponent($this->getGatewayClass(), $this);
- }
-
- /**
- * Set implementation class of ActiveRecordGateway
- * @param string $value
- */
- public function setGatewayClass($value)
- {
- $this->_gatewayClass = (string)$value;
- }
-
- /**
- * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
- */
- public function getGatewayClass()
- {
- return $this->_gatewayClass;
- }
-
- /**
- * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
- * @since 3.1.5
- * @see setInvalidFinderResult
- */
- public function getInvalidFinderResult()
- {
- return $this->_invalidFinderResult;
- }
-
- /**
- * Define the way an active record finder react if an invalid magic-finder invoked
- * @param TActiveRecordInvalidFinderResult
- * @since 3.1.5
- * @see getInvalidFinderResult
- */
- public function setInvalidFinderResult($value)
- {
- $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.TDbConnection');
+Prado::using('System.Data.ActiveRecord.TActiveRecord');
+Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException');
+Prado::using('System.Data.ActiveRecord.TActiveRecordGateway');
+
+/**
+ * TActiveRecordManager provides the default DB connection,
+ * default active record gateway, and table meta data inspector.
+ *
+ * The default connection can be set as follows:
+ *
+ * TActiveRecordManager::getInstance()->setDbConnection($conn);
+ *
+ * All new active record created after setting the
+ * {@link DbConnection setDbConnection()} will use that connection unless
+ * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection().
+ *
+ * Set the {@link setCache Cache} property to an ICache object to allow
+ * the active record gateway to cache the table meta data information.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordManager extends TComponent
+{
+ const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
+
+ /**
+ * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+ * @var string
+ */
+ private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
+
+ private $_gateway;
+ private $_meta=array();
+ private $_connection;
+
+ private $_cache;
+
+ /**
+ * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+ *
+ * @var TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ */
+ private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+ /**
+ * @return ICache application cache.
+ */
+ public function getCache()
+ {
+ return $this->_cache;
+ }
+
+ /**
+ * @param ICache application cache
+ */
+ public function setCache($value)
+ {
+ $this->_cache=$value;
+ }
+
+ /**
+ * @param TDbConnection default database connection
+ */
+ public function setDbConnection($conn)
+ {
+ $this->_connection=$conn;
+ }
+
+ /**
+ * @return TDbConnection default database connection
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * @return TActiveRecordManager static instance of record manager.
+ */
+ public static function getInstance($self=null)
+ {
+ static $instance;
+ if($self!==null)
+ $instance=$self;
+ else if($instance===null)
+ $instance = new self;
+ return $instance;
+ }
+
+ /**
+ * @return TActiveRecordGateway record gateway.
+ */
+ public function getRecordGateway()
+ {
+ if($this->_gateway === null) {
+ $this->_gateway = $this->createRecordGateway();
+ }
+ return $this->_gateway;
+ }
+
+ /**
+ * @return TActiveRecordGateway default record gateway.
+ */
+ protected function createRecordGateway()
+ {
+ return Prado::createComponent($this->getGatewayClass(), $this);
+ }
+
+ /**
+ * Set implementation class of ActiveRecordGateway
+ * @param string $value
+ */
+ public function setGatewayClass($value)
+ {
+ $this->_gatewayClass = (string)$value;
+ }
+
+ /**
+ * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+ */
+ public function getGatewayClass()
+ {
+ return $this->_gatewayClass;
+ }
+
+ /**
+ * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+ * @since 3.1.5
+ * @see setInvalidFinderResult
+ */
+ public function getInvalidFinderResult()
+ {
+ return $this->_invalidFinderResult;
+ }
+
+ /**
+ * Define the way an active record finder react if an invalid magic-finder invoked
+ * @param TActiveRecordInvalidFinderResult
+ * @since 3.1.5
+ * @see getInvalidFinderResult
+ */
+ public function setInvalidFinderResult($value)
+ {
+ $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+ }
+}
diff --git a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php
index 8a1e4dd6..3485adaa 100644
--- a/framework/Data/Common/Mssql/TMssqlCommandBuilder.php
+++ b/framework/Data/Common/Mssql/TMssqlCommandBuilder.php
@@ -1,173 +1,173 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- */
-
-Prado::using('System.Data.Common.TDbCommandBuilder');
-
-/**
- * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands
- * for MSSQL servers.
- *
- * @author Wei Zhuo
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TMssqlCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Overrides parent implementation. Uses "SELECT @@Identity".
- * @return integer last insert id, null if none is found.
- */
- public function getLastInsertID()
- {
- foreach($this->getTableInfo()->getColumns() as $column)
- {
- if($column->hasSequence())
- {
- $command = $this->getDbConnection()->createCommand('SELECT @@Identity');
- return intval($command->queryScalar());
- }
- }
- }
-
- /**
- * Overrides parent implementation. Alters the sql to apply $limit and $offset.
- * The idea for limit with offset is done by modifying the sql on the fly
- * with numerous assumptions on the structure of the sql string.
- * The modification is done with reference to the notes from
- * http://troels.arvin.dk/db/rdbms/#select-limit-offset
- *
- *
- * SELECT * FROM (
- * SELECT TOP n * FROM (
- * SELECT TOP z columns -- (z=n+skip)
- * FROM tablename
- * ORDER BY key ASC
- * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
- * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
- *
- *
- * Regular expressions are used to alter the SQL query. The resulting SQL query
- * may be malformed for complex queries. The following restrictions apply
- *
- *
- * -
- * In particular, commas should NOT
- * be used as part of the ordering expression or identifier. Commas must only be
- * used for separating the ordering clauses.
- *
- * -
- * In the ORDER BY clause, the column name should NOT be be qualified
- * with a table name or view name. Alias the column names or use column index.
- *
- * -
- * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
- *
- *
- *
- * @param string SQL query string.
- * @param integer maximum number of rows, -1 to ignore limit.
- * @param integer row offset, -1 to ignore offset.
- * @return string SQL with limit and offset.
- */
- public function applyLimitOffset($sql, $limit=-1, $offset=-1)
- {
- $limit = $limit!==null ? intval($limit) : -1;
- $offset = $offset!==null ? intval($offset) : -1;
- if ($limit > 0 && $offset <= 0) //just limit
- $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql);
- else if($limit > 0 && $offset > 0)
- $sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset);
- return $sql;
- }
-
- /**
- * Rewrite sql to apply $limit > and $offset > 0 for MSSQL database.
- * See http://troels.arvin.dk/db/rdbms/#select-limit-offset
- * @param string sql query
- * @param integer $limit > 0
- * @param integer $offset > 0
- * @return sql modified sql query applied with limit and offset.
- */
- protected function rewriteLimitOffsetSql($sql, $limit, $offset)
- {
- $fetch = $limit+$offset;
- $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql);
- $ordering = $this->findOrdering($sql);
-
- $orginalOrdering = $this->joinOrdering($ordering);
- $reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering));
- $sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner top table__] {$reverseOrdering}) as [__outer top table__] {$orginalOrdering}";
- return $sql;
- }
-
- /**
- * Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx
- *
- * @param string $sql
- * @return array ordering expression as key and ordering direction as value
- */
- protected function findOrdering($sql)
- {
- if(!preg_match('/ORDER BY/i', $sql))
- return array();
- $matches=array();
- $ordering=array();
- preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches);
- if(count($matches)>1 && count($matches[2]) > 0)
- {
- $parts = explode(',', $matches[2][0]);
- foreach($parts as $part)
- {
- $subs=array();
- if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs))
- {
- if(count($subs) > 1 && count($subs[2]) > 0)
- {
- $ordering[$subs[1][0]] = $subs[2][0];
- }
- //else what?
- }
- else
- $ordering[trim($part)] = 'ASC';
- }
- }
- return $ordering;
- }
-
- /**
- * @param array ordering obtained from findOrdering()
- * @return string concat the orderings
- */
- protected function joinOrdering($orders)
- {
- if(count($orders)>0)
- {
- $str=array();
- foreach($orders as $column => $direction)
- $str[] = $column.' '.$direction;
- return 'ORDER BY '.implode(', ', $str);
- }
- }
-
- /**
- * @param array original ordering
- * @return array ordering with reversed direction.
- */
- protected function reverseDirection($orders)
- {
- foreach($orders as $column => $direction)
- $orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC';
- return $orders;
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ */
+
+Prado::using('System.Data.Common.TDbCommandBuilder');
+
+/**
+ * TMssqlCommandBuilder provides specifics methods to create limit/offset query commands
+ * for MSSQL servers.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TMssqlCommandBuilder extends TDbCommandBuilder
+{
+ /**
+ * Overrides parent implementation. Uses "SELECT @@Identity".
+ * @return integer last insert id, null if none is found.
+ */
+ public function getLastInsertID()
+ {
+ foreach($this->getTableInfo()->getColumns() as $column)
+ {
+ if($column->hasSequence())
+ {
+ $command = $this->getDbConnection()->createCommand('SELECT @@Identity');
+ return intval($command->queryScalar());
+ }
+ }
+ }
+
+ /**
+ * Overrides parent implementation. Alters the sql to apply $limit and $offset.
+ * The idea for limit with offset is done by modifying the sql on the fly
+ * with numerous assumptions on the structure of the sql string.
+ * The modification is done with reference to the notes from
+ * http://troels.arvin.dk/db/rdbms/#select-limit-offset
+ *
+ *
+ * SELECT * FROM (
+ * SELECT TOP n * FROM (
+ * SELECT TOP z columns -- (z=n+skip)
+ * FROM tablename
+ * ORDER BY key ASC
+ * ) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
+ * ) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
+ *
+ *
+ * Regular expressions are used to alter the SQL query. The resulting SQL query
+ * may be malformed for complex queries. The following restrictions apply
+ *
+ *
+ * -
+ * In particular, commas should NOT
+ * be used as part of the ordering expression or identifier. Commas must only be
+ * used for separating the ordering clauses.
+ *
+ * -
+ * In the ORDER BY clause, the column name should NOT be be qualified
+ * with a table name or view name. Alias the column names or use column index.
+ *
+ * -
+ * No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
+ *
+ *
+ *
+ * @param string SQL query string.
+ * @param integer maximum number of rows, -1 to ignore limit.
+ * @param integer row offset, -1 to ignore offset.
+ * @return string SQL with limit and offset.
+ */
+ public function applyLimitOffset($sql, $limit=-1, $offset=-1)
+ {
+ $limit = $limit!==null ? intval($limit) : -1;
+ $offset = $offset!==null ? intval($offset) : -1;
+ if ($limit > 0 && $offset <= 0) //just limit
+ $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql);
+ else if($limit > 0 && $offset > 0)
+ $sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset);
+ return $sql;
+ }
+
+ /**
+ * Rewrite sql to apply $limit > and $offset > 0 for MSSQL database.
+ * See http://troels.arvin.dk/db/rdbms/#select-limit-offset
+ * @param string sql query
+ * @param integer $limit > 0
+ * @param integer $offset > 0
+ * @return sql modified sql query applied with limit and offset.
+ */
+ protected function rewriteLimitOffsetSql($sql, $limit, $offset)
+ {
+ $fetch = $limit+$offset;
+ $sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql);
+ $ordering = $this->findOrdering($sql);
+
+ $orginalOrdering = $this->joinOrdering($ordering);
+ $reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering));
+ $sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner top table__] {$reverseOrdering}) as [__outer top table__] {$orginalOrdering}";
+ return $sql;
+ }
+
+ /**
+ * Base on simplified syntax http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx
+ *
+ * @param string $sql
+ * @return array ordering expression as key and ordering direction as value
+ */
+ protected function findOrdering($sql)
+ {
+ if(!preg_match('/ORDER BY/i', $sql))
+ return array();
+ $matches=array();
+ $ordering=array();
+ preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches);
+ if(count($matches)>1 && count($matches[2]) > 0)
+ {
+ $parts = explode(',', $matches[2][0]);
+ foreach($parts as $part)
+ {
+ $subs=array();
+ if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs))
+ {
+ if(count($subs) > 1 && count($subs[2]) > 0)
+ {
+ $ordering[$subs[1][0]] = $subs[2][0];
+ }
+ //else what?
+ }
+ else
+ $ordering[trim($part)] = 'ASC';
+ }
+ }
+ return $ordering;
+ }
+
+ /**
+ * @param array ordering obtained from findOrdering()
+ * @return string concat the orderings
+ */
+ protected function joinOrdering($orders)
+ {
+ if(count($orders)>0)
+ {
+ $str=array();
+ foreach($orders as $column => $direction)
+ $str[] = $column.' '.$direction;
+ return 'ORDER BY '.implode(', ', $str);
+ }
+ }
+
+ /**
+ * @param array original ordering
+ * @return array ordering with reversed direction.
+ */
+ protected function reverseDirection($orders)
+ {
+ foreach($orders as $column => $direction)
+ $orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC';
+ return $orders;
+ }
+}
+
diff --git a/framework/Data/Common/Mssql/TMssqlTableColumn.php b/framework/Data/Common/Mssql/TMssqlTableColumn.php
index 5621bb6b..811d0f6e 100644
--- a/framework/Data/Common/Mssql/TMssqlTableColumn.php
+++ b/framework/Data/Common/Mssql/TMssqlTableColumn.php
@@ -1,64 +1,64 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common.Mssql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a Mssql database table.
- *
- * @author Wei Zhuo
- * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common.Mssql
- * @since 3.1
- */
-class TMssqlTableColumn extends TDbTableColumn
-{
- private static $types = array();
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
-
- return 'string';
- }
-
- /**
- * @return boolean true if the column has identity (auto-increment)
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement',false);
- }
-
- /**
- * @return boolean true if auto increments.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-
- /**
- * @return boolean true if db type is 'timestamp'.
- */
- public function getIsExcluded()
- {
- return strtolower($this->getDbType())==='timestamp';
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common.Mssql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a Mssql database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TMssqlTableColumn.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common.Mssql
+ * @since 3.1
+ */
+class TMssqlTableColumn extends TDbTableColumn
+{
+ private static $types = array();
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+
+ return 'string';
+ }
+
+ /**
+ * @return boolean true if the column has identity (auto-increment)
+ */
+ public function getAutoIncrement()
+ {
+ return $this->getInfo('AutoIncrement',false);
+ }
+
+ /**
+ * @return boolean true if auto increments.
+ */
+ public function hasSequence()
+ {
+ return $this->getAutoIncrement();
+ }
+
+ /**
+ * @return boolean true if db type is 'timestamp'.
+ */
+ public function getIsExcluded()
+ {
+ return strtolower($this->getDbType())==='timestamp';
+ }
+}
+
diff --git a/framework/Data/Common/Mssql/TMssqlTableInfo.php b/framework/Data/Common/Mssql/TMssqlTableInfo.php
index c65e3eaa..3b48e42d 100644
--- a/framework/Data/Common/Mssql/TMssqlTableInfo.php
+++ b/framework/Data/Common/Mssql/TMssqlTableInfo.php
@@ -1,64 +1,64 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Mssql
- */
-
-/**
- * Loads the base TDbTableInfo class and TMssqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Mssql.TMssqlTableColumn');
-
-/**
- * TMssqlTableInfo class provides additional table information for Mssql database.
- *
- * @author Wei Zhuo
- * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Mssql
- * @since 3.1
- */
-class TMssqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string catalog name (database name)
- */
- public function getCatalogName()
- {
- return $this->getInfo('CatalogName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- //MSSQL alway returns the catalog, schem and table names.
- return '['.$this->getCatalogName().'].['.$this->getSchemaName().'].['.$this->getTableName().']';
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Mssql.TMssqlCommandBuilder');
- return new TMssqlCommandBuilder($connection,$this);
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Mssql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TMssqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Mssql.TMssqlTableColumn');
+
+/**
+ * TMssqlTableInfo class provides additional table information for Mssql database.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TMssqlTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Mssql
+ * @since 3.1
+ */
+class TMssqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ /**
+ * @return string catalog name (database name)
+ */
+ public function getCatalogName()
+ {
+ return $this->getInfo('CatalogName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ //MSSQL alway returns the catalog, schem and table names.
+ return '['.$this->getCatalogName().'].['.$this->getSchemaName().'].['.$this->getTableName().']';
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.Mssql.TMssqlCommandBuilder');
+ return new TMssqlCommandBuilder($connection,$this);
+ }
+}
+
diff --git a/framework/Data/Common/Mysql/TMysqlCommandBuilder.php b/framework/Data/Common/Mysql/TMysqlCommandBuilder.php
index fa2b2c51..58854bbc 100644
--- a/framework/Data/Common/Mysql/TMysqlCommandBuilder.php
+++ b/framework/Data/Common/Mysql/TMysqlCommandBuilder.php
@@ -1,26 +1,26 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- */
-
-Prado::using('System.Data.Common.TDbCommandBuilder');
-
-/**
- * TMysqlCommandBuilder implements default TDbCommandBuilder
- *
- * @author Wei Zhuo
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TMysqlCommandBuilder extends TDbCommandBuilder
-{
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ */
+
+Prado::using('System.Data.Common.TDbCommandBuilder');
+
+/**
+ * TMysqlCommandBuilder implements default TDbCommandBuilder
+ *
+ * @author Wei Zhuo
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TMysqlCommandBuilder extends TDbCommandBuilder
+{
+}
+
diff --git a/framework/Data/Common/Mysql/TMysqlMetaData.php b/framework/Data/Common/Mysql/TMysqlMetaData.php
index f9824015..151111af 100644
--- a/framework/Data/Common/Mysql/TMysqlMetaData.php
+++ b/framework/Data/Common/Mysql/TMysqlMetaData.php
@@ -1,386 +1,386 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Mysql.TMysqlTableInfo');
-
-/**
- * TMysqlMetaData loads Mysql version 4.1.x and 5.x database table and column information.
- *
- * For Mysql version 4.1.x, PHP 5.1.3 or later is required.
- * See http://netevil.org/node.php?nid=795&SC=1
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlMetaData extends TDbMetaData
-{
- private $_serverVersion=0;
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TMysqlTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, '`', '`');
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '`', '`');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '`', '`');
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TMysqlTableInfo table information.
- */
- protected function createTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
- $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
- $this->getDbConnection()->setActive(true);
- $sql = "SHOW FULL FIELDS FROM {$find}";
- $command = $this->getDbConnection()->createCommand($sql);
- $tableInfo = $this->createNewTableInfo($table);
- $index=0;
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $this->processColumn($tableInfo,$col);
- }
- if($index===0)
- throw new TDbException('dbmetadata_invalid_table_view', $table);
- return $tableInfo;
- }
-
- /**
- * @return float server version.
- */
- protected function getServerVersion()
- {
- if(!$this->_serverVersion)
- {
- $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION);
- $digits=array();
- preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits);
- $this->_serverVersion=floatval($digits[1].'.'.$digits[2].$digits[3]);
- }
- return $this->_serverVersion;
- }
-
- /**
- * @param TMysqlTableInfo table information.
- * @param array column information.
- */
- protected function processColumn($tableInfo, $col)
- {
- $columnId = $col['Field'];
-
- $info['ColumnName'] = "`$columnId`"; //quote the column names!
- $info['ColumnId'] = $columnId;
- $info['ColumnIndex'] = $col['index'];
- if($col['Null']==='YES')
- $info['AllowNull'] = true;
- if(is_int(strpos(strtolower($col['Extra']), 'auto_increment')))
- $info['AutoIncrement']=true;
- if($col['Default']!=="")
- $info['DefaultValue'] = $col['Default'];
-
- if($col['Key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys()))
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $tableInfo))
- $info['IsForeignKey'] = true;
-
- $info['DbType'] = $col['Type'];
- $match=array();
- //find SET/ENUM values, column size, precision, and scale
- if(preg_match('/\((.*)\)/', $col['Type'], $match))
- {
- $info['DbType']= preg_replace('/\(.*\)/', '', $col['Type']);
-
- //find SET/ENUM values
- if($this->isEnumSetType($info['DbType']))
- $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY);
-
- //find column size, precision and scale
- $pscale = array();
- if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale))
- {
- if($this->isPrecisionType($info['DbType']))
- {
- $info['NumericPrecision'] = intval($pscale[1]);
- if(count($pscale) > 2)
- $info['NumericScale'] = intval($pscale[2]);
- }
- else
- $info['ColumnSize'] = intval($pscale[1]);
- }
- }
-
- $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info);
- }
-
- /**
- * @return boolean true if column type if "numeric", "interval" or begins with "time".
- */
- protected function isPrecisionType($type)
- {
- $type = strtolower(trim($type));
- return $type==='decimal' || $type==='dec'
- || $type==='float' || $type==='double'
- || $type==='double precision' || $type==='real';
- }
-
- /**
- * @return boolean true if column type if "enum" or "set".
- */
- protected function isEnumSetType($type)
- {
- $type = strtolower(trim($type));
- return $type==='set' || $type==='enum';
- }
-
- /**
- * @param string table name, may be quoted with back-ticks and may contain database name.
- * @return array tuple ($schema,$table), $schema may be null.
- * @throws TDbException when table name contains invalid identifier bytes.
- */
- protected function getSchemaTableName($table)
- {
- //remove the back ticks and separate out the "database.table"
- $result = explode('.', str_replace('`', '', $table));
- foreach($result as $name)
- {
- if(!$this->isValidIdentifier($name))
- {
- $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html';
- throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref);
- }
- }
- return count($result) > 1 ? $result : array(null, $result[0]);
- }
-
- /**
- * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
- * @param string identifier name
- * @param boolean true if valid identifier.
- */
- protected function isValidIdentifier($name)
- {
- return !preg_match('#/|\\|.|\x00|\xFF#', $name);
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return TMysqlTableInfo
- */
- protected function createNewTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
- $info['SchemaName'] = $schemaName;
- $info['TableName'] = $tableName;
- if($this->getIsView($schemaName,$tableName))
- $info['IsView'] = true;
- list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
- $class = $this->getTableInfoClass();
- return new $class($info,$primary,$foreign);
- }
-
- /**
- * For MySQL version 5.0.1 or later we can use SHOW FULL TABLES
- * http://dev.mysql.com/doc/refman/5.0/en/show-tables.html
- *
- * For MySQL version 5.0.1 or ealier, this always return false.
- * @param string database name, null to use default connection database.
- * @param string table or view name.
- * @return boolean true if is view, false otherwise.
- * @throws TDbException if table or view does not exist.
- */
- protected function getIsView($schemaName,$tableName)
- {
- if($this->getServerVersion()<5.01)
- return false;
- if($schemaName!==null)
- $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table";
- else
- $sql = "SHOW FULL TABLES LIKE :table";
-
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- try
- {
- return count($result = $command->queryRow()) > 0 && $result['Table_type']==='VIEW';
- }
- catch(TDbException $e)
- {
- $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName;
- throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage());
- }
- }
-
- /**
- * Gets the primary and foreign key column details for the given table.
- * @param string schema name
- * @param string table name.
- * @return array tuple ($primary, $foreign)
- */
- protected function getConstraintKeys($schemaName, $tableName)
- {
- $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
- $sql = "SHOW INDEX FROM {$table}";
- $command = $this->getDbConnection()->createCommand($sql);
- $primary = array();
- foreach($command->query() as $row)
- {
- if($row['Key_name']==='PRIMARY')
- $primary[] = $row['Column_name'];
- }
- // MySQL version was increased to >=5.1.21 instead of 5.x
- // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588)
- if($this->getServerVersion() >= 5.121)
- $foreign = $this->getForeignConstraints($schemaName,$tableName);
- else
- $foreign = $this->findForeignConstraints($schemaName,$tableName);
- return array($primary,$foreign);
- }
-
- /**
- * Gets foreign relationship constraint keys and table name
- * @param string database name
- * @param string table name
- * @return array foreign relationship table name and keys.
- */
- protected function getForeignConstraints($schemaName, $tableName)
- {
- $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA LIKE :schema' : 'AND TABLE_SCHEMA LIKE DATABASE()';
- $sql = <<getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- if($schemaName!==null)
- $command->bindValue(':schema', $schemaName);
- $fkeys=array();
- foreach($command->query() as $col)
- {
- $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol'];
- $fkeys[$col['con']]['table'] = $col['fktable'];
- }
- return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
- }
-
- /**
- * @param string database name
- * @param string table name
- * @return string SQL command to create the table.
- * @throws TDbException if PHP version is less than 5.1.3
- */
- protected function getShowCreateTable($schemaName, $tableName)
- {
- if(version_compare(PHP_VERSION,'5.1.3','<'))
- throw new TDbException('dbmetadata_requires_php_version', 'Mysql 4.1.x', '5.1.3');
-
- //See http://netevil.org/node.php?nid=795&SC=1
- $this->getDbConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
- if($schemaName!==null)
- $sql = "SHOW CREATE TABLE `{$schemaName}`.`{$tableName}`";
- else
- $sql = "SHOW CREATE TABLE `{$tableName}`";
- $command = $this->getDbConnection()->createCommand($sql);
- $result = $command->queryRow();
- return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : '');
- }
-
- /**
- * Extract foreign key constraints by extracting the contraints from SHOW CREATE TABLE result.
- * @param string database name
- * @param string table name
- * @return array foreign relationship table name and keys.
- */
- protected function findForeignConstraints($schemaName, $tableName)
- {
- $sql = $this->getShowCreateTable($schemaName, $tableName);
- $matches =array();
- $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+`?([^`]+)`?\s\(([^\)]+)\)/mi';
- preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER);
- $foreign = array();
- foreach($matches as $match)
- {
- $fields = array_map('trim',explode(',',str_replace('`','',$match[1])));
- $fk_fields = array_map('trim',explode(',',str_replace('`','',$match[3])));
- $keys=array();
- foreach($fields as $k=>$v)
- $keys[$v] = $fk_fields[$k];
- $foreign[] = array('keys' => $keys, 'table' => trim($match[2]));
- }
- return $foreign;
- }
-
- /**
- * @param string column name.
- * @param TPgsqlTableInfo table information.
- * @return boolean true if column is a foreign key.
- */
- protected function isForeignKeyColumn($columnId, $tableInfo)
- {
- foreach($tableInfo->getForeignKeys() as $fk)
- {
- if(in_array($columnId, array_keys($fk['keys'])))
- return true;
- }
- return false;
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ */
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Mysql.TMysqlTableInfo');
+
+/**
+ * TMysqlMetaData loads Mysql version 4.1.x and 5.x database table and column information.
+ *
+ * For Mysql version 4.1.x, PHP 5.1.3 or later is required.
+ * See http://netevil.org/node.php?nid=795&SC=1
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ * @since 3.1
+ */
+class TMysqlMetaData extends TDbMetaData
+{
+ private $_serverVersion=0;
+
+ /**
+ * @return string TDbTableInfo class name.
+ */
+ protected function getTableInfoClass()
+ {
+ return 'TMysqlTableInfo';
+ }
+
+ /**
+ * Quotes a table name for use in a query.
+ * @param string $name table name
+ * @return string the properly quoted table name
+ */
+ public function quoteTableName($name)
+ {
+ return parent::quoteTableName($name, '`', '`');
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * @param string $name column name
+ * @return string the properly quoted column name
+ */
+ public function quoteColumnName($name)
+ {
+ return parent::quoteColumnName($name, '`', '`');
+ }
+
+ /**
+ * Quotes a column alias for use in a query.
+ * @param string $name column alias
+ * @return string the properly quoted column alias
+ */
+ public function quoteColumnAlias($name)
+ {
+ return parent::quoteColumnAlias($name, '`', '`');
+ }
+
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return TMysqlTableInfo table information.
+ */
+ protected function createTableInfo($table)
+ {
+ list($schemaName,$tableName) = $this->getSchemaTableName($table);
+ $find = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
+ $this->getDbConnection()->setActive(true);
+ $sql = "SHOW FULL FIELDS FROM {$find}";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $tableInfo = $this->createNewTableInfo($table);
+ $index=0;
+ foreach($command->query() as $col)
+ {
+ $col['index'] = $index++;
+ $this->processColumn($tableInfo,$col);
+ }
+ if($index===0)
+ throw new TDbException('dbmetadata_invalid_table_view', $table);
+ return $tableInfo;
+ }
+
+ /**
+ * @return float server version.
+ */
+ protected function getServerVersion()
+ {
+ if(!$this->_serverVersion)
+ {
+ $version = $this->getDbConnection()->getAttribute(PDO::ATTR_SERVER_VERSION);
+ $digits=array();
+ preg_match('/(\d+)\.(\d+)\.(\d+)/', $version, $digits);
+ $this->_serverVersion=floatval($digits[1].'.'.$digits[2].$digits[3]);
+ }
+ return $this->_serverVersion;
+ }
+
+ /**
+ * @param TMysqlTableInfo table information.
+ * @param array column information.
+ */
+ protected function processColumn($tableInfo, $col)
+ {
+ $columnId = $col['Field'];
+
+ $info['ColumnName'] = "`$columnId`"; //quote the column names!
+ $info['ColumnId'] = $columnId;
+ $info['ColumnIndex'] = $col['index'];
+ if($col['Null']==='YES')
+ $info['AllowNull'] = true;
+ if(is_int(strpos(strtolower($col['Extra']), 'auto_increment')))
+ $info['AutoIncrement']=true;
+ if($col['Default']!=="")
+ $info['DefaultValue'] = $col['Default'];
+
+ if($col['Key']==='PRI' || in_array($columnId, $tableInfo->getPrimaryKeys()))
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $tableInfo))
+ $info['IsForeignKey'] = true;
+
+ $info['DbType'] = $col['Type'];
+ $match=array();
+ //find SET/ENUM values, column size, precision, and scale
+ if(preg_match('/\((.*)\)/', $col['Type'], $match))
+ {
+ $info['DbType']= preg_replace('/\(.*\)/', '', $col['Type']);
+
+ //find SET/ENUM values
+ if($this->isEnumSetType($info['DbType']))
+ $info['DbTypeValues'] = preg_split("/[',]/S", $match[1], -1, PREG_SPLIT_NO_EMPTY);
+
+ //find column size, precision and scale
+ $pscale = array();
+ if(preg_match('/(\d+)(?:,(\d+))?+/', $match[1], $pscale))
+ {
+ if($this->isPrecisionType($info['DbType']))
+ {
+ $info['NumericPrecision'] = intval($pscale[1]);
+ if(count($pscale) > 2)
+ $info['NumericScale'] = intval($pscale[2]);
+ }
+ else
+ $info['ColumnSize'] = intval($pscale[1]);
+ }
+ }
+
+ $tableInfo->Columns[$columnId] = new TMysqlTableColumn($info);
+ }
+
+ /**
+ * @return boolean true if column type if "numeric", "interval" or begins with "time".
+ */
+ protected function isPrecisionType($type)
+ {
+ $type = strtolower(trim($type));
+ return $type==='decimal' || $type==='dec'
+ || $type==='float' || $type==='double'
+ || $type==='double precision' || $type==='real';
+ }
+
+ /**
+ * @return boolean true if column type if "enum" or "set".
+ */
+ protected function isEnumSetType($type)
+ {
+ $type = strtolower(trim($type));
+ return $type==='set' || $type==='enum';
+ }
+
+ /**
+ * @param string table name, may be quoted with back-ticks and may contain database name.
+ * @return array tuple ($schema,$table), $schema may be null.
+ * @throws TDbException when table name contains invalid identifier bytes.
+ */
+ protected function getSchemaTableName($table)
+ {
+ //remove the back ticks and separate out the "database.table"
+ $result = explode('.', str_replace('`', '', $table));
+ foreach($result as $name)
+ {
+ if(!$this->isValidIdentifier($name))
+ {
+ $ref = 'http://dev.mysql.com/doc/refman/5.0/en/identifiers.html';
+ throw new TDbException('dbcommon_invalid_identifier_name', $table, $ref);
+ }
+ }
+ return count($result) > 1 ? $result : array(null, $result[0]);
+ }
+
+ /**
+ * http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
+ * @param string identifier name
+ * @param boolean true if valid identifier.
+ */
+ protected function isValidIdentifier($name)
+ {
+ return !preg_match('#/|\\|.|\x00|\xFF#', $name);
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return TMysqlTableInfo
+ */
+ protected function createNewTableInfo($table)
+ {
+ list($schemaName,$tableName) = $this->getSchemaTableName($table);
+ $info['SchemaName'] = $schemaName;
+ $info['TableName'] = $tableName;
+ if($this->getIsView($schemaName,$tableName))
+ $info['IsView'] = true;
+ list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
+ $class = $this->getTableInfoClass();
+ return new $class($info,$primary,$foreign);
+ }
+
+ /**
+ * For MySQL version 5.0.1 or later we can use SHOW FULL TABLES
+ * http://dev.mysql.com/doc/refman/5.0/en/show-tables.html
+ *
+ * For MySQL version 5.0.1 or ealier, this always return false.
+ * @param string database name, null to use default connection database.
+ * @param string table or view name.
+ * @return boolean true if is view, false otherwise.
+ * @throws TDbException if table or view does not exist.
+ */
+ protected function getIsView($schemaName,$tableName)
+ {
+ if($this->getServerVersion()<5.01)
+ return false;
+ if($schemaName!==null)
+ $sql = "SHOW FULL TABLES FROM `{$schemaName}` LIKE :table";
+ else
+ $sql = "SHOW FULL TABLES LIKE :table";
+
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ try
+ {
+ return count($result = $command->queryRow()) > 0 && $result['Table_type']==='VIEW';
+ }
+ catch(TDbException $e)
+ {
+ $table = $schemaName===null?$tableName:$schemaName.'.'.$tableName;
+ throw new TDbException('dbcommon_invalid_table_name',$table,$e->getMessage());
+ }
+ }
+
+ /**
+ * Gets the primary and foreign key column details for the given table.
+ * @param string schema name
+ * @param string table name.
+ * @return array tuple ($primary, $foreign)
+ */
+ protected function getConstraintKeys($schemaName, $tableName)
+ {
+ $table = $schemaName===null ? "`{$tableName}`" : "`{$schemaName}`.`{$tableName}`";
+ $sql = "SHOW INDEX FROM {$table}";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $primary = array();
+ foreach($command->query() as $row)
+ {
+ if($row['Key_name']==='PRIMARY')
+ $primary[] = $row['Column_name'];
+ }
+ // MySQL version was increased to >=5.1.21 instead of 5.x
+ // due to a MySQL bug (http://bugs.mysql.com/bug.php?id=19588)
+ if($this->getServerVersion() >= 5.121)
+ $foreign = $this->getForeignConstraints($schemaName,$tableName);
+ else
+ $foreign = $this->findForeignConstraints($schemaName,$tableName);
+ return array($primary,$foreign);
+ }
+
+ /**
+ * Gets foreign relationship constraint keys and table name
+ * @param string database name
+ * @param string table name
+ * @return array foreign relationship table name and keys.
+ */
+ protected function getForeignConstraints($schemaName, $tableName)
+ {
+ $andSchema = $schemaName !== null ? 'AND TABLE_SCHEMA LIKE :schema' : 'AND TABLE_SCHEMA LIKE DATABASE()';
+ $sql = <<getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ if($schemaName!==null)
+ $command->bindValue(':schema', $schemaName);
+ $fkeys=array();
+ foreach($command->query() as $col)
+ {
+ $fkeys[$col['con']]['keys'][$col['col']] = $col['fkcol'];
+ $fkeys[$col['con']]['table'] = $col['fktable'];
+ }
+ return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
+ }
+
+ /**
+ * @param string database name
+ * @param string table name
+ * @return string SQL command to create the table.
+ * @throws TDbException if PHP version is less than 5.1.3
+ */
+ protected function getShowCreateTable($schemaName, $tableName)
+ {
+ if(version_compare(PHP_VERSION,'5.1.3','<'))
+ throw new TDbException('dbmetadata_requires_php_version', 'Mysql 4.1.x', '5.1.3');
+
+ //See http://netevil.org/node.php?nid=795&SC=1
+ $this->getDbConnection()->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+ if($schemaName!==null)
+ $sql = "SHOW CREATE TABLE `{$schemaName}`.`{$tableName}`";
+ else
+ $sql = "SHOW CREATE TABLE `{$tableName}`";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $result = $command->queryRow();
+ return isset($result['Create Table']) ? $result['Create Table'] : (isset($result['Create View']) ? $result['Create View'] : '');
+ }
+
+ /**
+ * Extract foreign key constraints by extracting the contraints from SHOW CREATE TABLE result.
+ * @param string database name
+ * @param string table name
+ * @return array foreign relationship table name and keys.
+ */
+ protected function findForeignConstraints($schemaName, $tableName)
+ {
+ $sql = $this->getShowCreateTable($schemaName, $tableName);
+ $matches =array();
+ $regexp = '/FOREIGN KEY\s+\(([^\)]+)\)\s+REFERENCES\s+`?([^`]+)`?\s\(([^\)]+)\)/mi';
+ preg_match_all($regexp,$sql,$matches,PREG_SET_ORDER);
+ $foreign = array();
+ foreach($matches as $match)
+ {
+ $fields = array_map('trim',explode(',',str_replace('`','',$match[1])));
+ $fk_fields = array_map('trim',explode(',',str_replace('`','',$match[3])));
+ $keys=array();
+ foreach($fields as $k=>$v)
+ $keys[$v] = $fk_fields[$k];
+ $foreign[] = array('keys' => $keys, 'table' => trim($match[2]));
+ }
+ return $foreign;
+ }
+
+ /**
+ * @param string column name.
+ * @param TPgsqlTableInfo table information.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $tableInfo)
+ {
+ foreach($tableInfo->getForeignKeys() as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
diff --git a/framework/Data/Common/Mysql/TMysqlTableColumn.php b/framework/Data/Common/Mysql/TMysqlTableColumn.php
index 34dceaad..901f4f54 100644
--- a/framework/Data/Common/Mysql/TMysqlTableColumn.php
+++ b/framework/Data/Common/Mysql/TMysqlTableColumn.php
@@ -1,72 +1,72 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a Mysql database table.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlTableColumn extends TDbTableColumn
-{
- private static $types = array(
- 'integer' => array('bit', 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'),
- 'boolean' => array('boolean', 'bool'),
- 'float' => array('float', 'double', 'double precision', 'decimal', 'dec', 'numeric', 'fixed')
- );
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($this->getDbType())));
- if($dbtype==='tinyint' && $this->getColumnSize()===1)
- return 'boolean';
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-
- /**
- * @return boolean true if column will auto-increment when the column value is inserted as null.
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement', false);
- }
-
- /**
- * @return boolean true if auto increment is true.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-
- public function getDbTypeValues()
- {
- return $this->getInfo('DbTypeValues');
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a Mysql database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ * @since 3.1
+ */
+class TMysqlTableColumn extends TDbTableColumn
+{
+ private static $types = array(
+ 'integer' => array('bit', 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'),
+ 'boolean' => array('boolean', 'bool'),
+ 'float' => array('float', 'double', 'double precision', 'decimal', 'dec', 'numeric', 'fixed')
+ );
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ $dbtype = trim(str_replace(array('unsigned', 'zerofill'),array('','',),strtolower($this->getDbType())));
+ if($dbtype==='tinyint' && $this->getColumnSize()===1)
+ return 'boolean';
+ foreach(self::$types as $type => $dbtypes)
+ {
+ if(in_array($dbtype, $dbtypes))
+ return $type;
+ }
+ return 'string';
+ }
+
+ /**
+ * @return boolean true if column will auto-increment when the column value is inserted as null.
+ */
+ public function getAutoIncrement()
+ {
+ return $this->getInfo('AutoIncrement', false);
+ }
+
+ /**
+ * @return boolean true if auto increment is true.
+ */
+ public function hasSequence()
+ {
+ return $this->getAutoIncrement();
+ }
+
+ public function getDbTypeValues()
+ {
+ return $this->getInfo('DbTypeValues');
+ }
+}
+
diff --git a/framework/Data/Common/Mysql/TMysqlTableInfo.php b/framework/Data/Common/Mysql/TMysqlTableInfo.php
index 30a3d0a1..40b421db 100644
--- a/framework/Data/Common/Mysql/TMysqlTableInfo.php
+++ b/framework/Data/Common/Mysql/TMysqlTableInfo.php
@@ -1,59 +1,59 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Mysql
- */
-
-/**
- * Loads the base TDbTableInfo class and TMysqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Mysql.TMysqlTableColumn');
-
-/**
- * TMysqlTableInfo class provides additional table information for MySQL database.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Mysql
- * @since 3.1
- */
-class TMysqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- if(($schema=$this->getSchemaName())!==null)
- return '`'.$schema.'`.`'.$this->getTableName().'`';
- else
- return '`'.$this->getTableName().'`';
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Mysql.TMysqlCommandBuilder');
- return new TMysqlCommandBuilder($connection,$this);
- }
-}
-
-?>
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TMysqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Mysql.TMysqlTableColumn');
+
+/**
+ * TMysqlTableInfo class provides additional table information for MySQL database.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Mysql
+ * @since 3.1
+ */
+class TMysqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ if(($schema=$this->getSchemaName())!==null)
+ return '`'.$schema.'`.`'.$this->getTableName().'`';
+ else
+ return '`'.$this->getTableName().'`';
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.Mysql.TMysqlCommandBuilder');
+ return new TMysqlCommandBuilder($connection,$this);
+ }
+}
+
+?>
diff --git a/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php b/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php
index 8f4147e5..f9938d61 100644
--- a/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php
+++ b/framework/Data/Common/Pgsql/TPgsqlCommandBuilder.php
@@ -1,69 +1,69 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- */
-
-Prado::using('System.Data.Common.TDbCommandBuilder');
-
-/**
- * TPgsqlCommandBuilder provides specifics methods to create limit/offset query commands
- * for Pgsql database.
- *
- * @author Wei Zhuo
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TPgsqlCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Overrides parent implementation. Only column of type text or character (and its variants)
- * accepts the LIKE criteria.
- * @param array list of column id for potential search condition.
- * @param string string of keywords
- * @return string SQL search condition matching on a set of columns.
- */
- public function getSearchExpression($fields, $keywords)
- {
- $columns = array();
- foreach($fields as $field)
- {
- if($this->isSearchableColumn($this->getTableInfo()->getColumn($field)))
- $columns[] = $field;
- }
- return parent::getSearchExpression($columns, $keywords);
- }
- /**
- *
- * @return boolean true if column can be used for LIKE searching.
- */
- protected function isSearchableColumn($column)
- {
- $type = strtolower($column->getDbType());
- return $type === 'character varying' || $type === 'varchar' ||
- $type === 'character' || $type === 'char' || $type === 'text';
- }
-
- /**
- * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive).
- * @param string column name.
- * @param array keywords
- * @return string search condition for all words in one column.
- */
- protected function getSearchCondition($column, $words)
- {
- $conditions=array();
- foreach($words as $word)
- $conditions[] = $column.' ILIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
- return '('.implode(' AND ', $conditions).')';
- }
-
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ */
+
+Prado::using('System.Data.Common.TDbCommandBuilder');
+
+/**
+ * TPgsqlCommandBuilder provides specifics methods to create limit/offset query commands
+ * for Pgsql database.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TPgsqlCommandBuilder extends TDbCommandBuilder
+{
+ /**
+ * Overrides parent implementation. Only column of type text or character (and its variants)
+ * accepts the LIKE criteria.
+ * @param array list of column id for potential search condition.
+ * @param string string of keywords
+ * @return string SQL search condition matching on a set of columns.
+ */
+ public function getSearchExpression($fields, $keywords)
+ {
+ $columns = array();
+ foreach($fields as $field)
+ {
+ if($this->isSearchableColumn($this->getTableInfo()->getColumn($field)))
+ $columns[] = $field;
+ }
+ return parent::getSearchExpression($columns, $keywords);
+ }
+ /**
+ *
+ * @return boolean true if column can be used for LIKE searching.
+ */
+ protected function isSearchableColumn($column)
+ {
+ $type = strtolower($column->getDbType());
+ return $type === 'character varying' || $type === 'varchar' ||
+ $type === 'character' || $type === 'char' || $type === 'text';
+ }
+
+ /**
+ * Overrides parent implementation to use PostgreSQL's ILIKE instead of LIKE (case-sensitive).
+ * @param string column name.
+ * @param array keywords
+ * @return string search condition for all words in one column.
+ */
+ protected function getSearchCondition($column, $words)
+ {
+ $conditions=array();
+ foreach($words as $word)
+ $conditions[] = $column.' ILIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
+ return '('.implode(' AND ', $conditions).')';
+ }
+
+}
+
diff --git a/framework/Data/Common/Pgsql/TPgsqlMetaData.php b/framework/Data/Common/Pgsql/TPgsqlMetaData.php
index 85785606..86a9892e 100644
--- a/framework/Data/Common/Pgsql/TPgsqlMetaData.php
+++ b/framework/Data/Common/Pgsql/TPgsqlMetaData.php
@@ -1,422 +1,422 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo');
-
-/**
- * TPgsqlMetaData loads PostgreSQL database table and column information.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlMetaData extends TDbMetaData
-{
- private $_defaultSchema = 'public';
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TPgsqlTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, '"', '"');
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '"', '"');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '"', '"');
- }
-
- /**
- * @param string default schema.
- */
- public function setDefaultSchema($schema)
- {
- $this->_defaultSchema=$schema;
- }
-
- /**
- * @return string default schema.
- */
- public function getDefaultSchema()
- {
- return $this->_defaultSchema;
- }
-
- /**
- * @param string table name with optional schema name prefix, uses default schema name prefix is not provided.
- * @return array tuple as ($schemaName,$tableName)
- */
- protected function getSchemaTableName($table)
- {
- if(count($parts= explode('.', str_replace('"','',$table))) > 1)
- return array($parts[0], $parts[1]);
- else
- return array($this->getDefaultSchema(),$parts[0]);
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TPgsqlTableInfo table information.
- */
- protected function createTableInfo($table)
- {
- list($schemaName,$tableName) = $this->getSchemaTableName($table);
-
- // This query is made much more complex by the addition of the 'attisserial' field.
- // The subquery to get that field checks to see if there is an internally dependent
- // sequence on the field.
- $sql =
-<< 0 AND NOT a.attisdropped
- ORDER BY a.attnum
-EOD;
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
- $tableInfo = $this->createNewTableInfo($schemaName, $tableName);
- $index=0;
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $this->processColumn($tableInfo, $col);
- }
- if($index===0)
- throw new TDbException('dbmetadata_invalid_table_view', $table);
- return $tableInfo;
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return TPgsqlTableInfo
- */
- protected function createNewTableInfo($schemaName,$tableName)
- {
- $info['SchemaName'] = $this->assertIdentifier($schemaName);
- $info['TableName'] = $this->assertIdentifier($tableName);
- if($this->getIsView($schemaName,$tableName))
- $info['IsView'] = true;
- list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
- $class = $this->getTableInfoClass();
- return new $class($info,$primary,$foreign);
- }
-
- /**
- * @param string table name, schema name or column name.
- * @return string a valid identifier.
- * @throws TDbException when table name contains a double quote (").
- */
- protected function assertIdentifier($name)
- {
- if(strpos($name, '"')!==false)
- {
- $ref = 'http://www.postgresql.org/docs/7.4/static/sql-syntax.html#SQL-SYNTAX-IDENTIFIERS';
- throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref);
- }
- return $name;
- }
-
- /**
- * @param string table schema name
- * @param string table name.
- * @return boolean true if the table is a view.
- */
- protected function getIsView($schemaName,$tableName)
- {
- $sql =
-<<getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':schema',$schemaName);
- $command->bindValue(':table', $tableName);
- return intval($command->queryScalar()) === 1;
- }
-
- /**
- * @param TPgsqlTableInfo table information.
- * @param array column information.
- */
- protected function processColumn($tableInfo, $col)
- {
- $columnId = $col['attname']; //use column name as column Id
-
- $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
- $info['ColumnId'] = $columnId;
- $info['ColumnIndex'] = $col['index'];
- if(!$col['attnotnull'])
- $info['AllowNull'] = true;
- if(in_array($columnId, $tableInfo->getPrimaryKeys()))
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $tableInfo))
- $info['IsForeignKey'] = true;
-
- if($col['atttypmod'] > 0)
- $info['ColumnSize'] = $col['atttypmod'] - 4;
- if($col['atthasdef'])
- $info['DefaultValue'] = $col['adsrc'];
- if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(')
- {
- if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null)
- {
- $info['SequenceName'] = $sequence;
- unset($info['DefaultValue']);
- }
- }
- $matches = array();
- if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches))
- {
- $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']);
- if($this->isPrecisionType($info['DbType']))
- {
- $info['NumericPrecision'] = intval($matches[1]);
- if(count($matches) > 2)
- $info['NumericScale'] = intval($matches[2]);
- }
- else
- $info['ColumnSize'] = intval($matches[1]);
- }
- else
- $info['DbType'] = $col['type'];
-
- $tableInfo->Columns[$columnId] = new TPgsqlTableColumn($info);
- }
-
- /**
- * @return string serial name if found, null otherwise.
- */
- protected function getSequenceName($tableInfo,$src)
- {
- $matches = array();
- if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches))
- {
- if(is_int(strpos($matches[1], '.')))
- return $matches[1];
- else
- return $tableInfo->getSchemaName().'.'.$matches[1];
- }
- }
-
- /**
- * @return boolean true if column type if "numeric", "interval" or begins with "time".
- */
- protected function isPrecisionType($type)
- {
- $type = strtolower(trim($type));
- return $type==='numeric' || $type==='interval' || strpos($type, 'time')===0;
- }
-
- /**
- * Gets the primary and foreign key column details for the given table.
- * @param string schema name
- * @param string table name.
- * @return array tuple ($primary, $foreign)
- */
- protected function getConstraintKeys($schemaName, $tableName)
- {
- $sql =
-<<getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
- $primary = array();
- $foreign = array();
- foreach($command->query() as $row)
- {
- switch($row['contype'])
- {
- case 'p':
- $primary = $this->getPrimaryKeys($tableName, $schemaName, $row['indkey']);
- break;
- case 'f':
- if(($fkey = $this->getForeignKeys($row['consrc']))!==null)
- $foreign[] = $fkey;
- break;
- }
- }
- return array($primary,$foreign);
- }
-
- /**
- * Gets the primary key field names
- * @param string pgsql primary key definition
- * @return array primary key field names.
- */
- protected function getPrimaryKeys($tableName, $schemaName, $columnIndex)
- {
- $index = join(', ', explode(' ', $columnIndex));
- $sql =
-<<getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- $command->bindValue(':schema', $schemaName);
-// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex)));
- $primary = array();
- foreach($command->query() as $row)
- {
- $primary[] = $row['attname'];
- }
-
- return $primary;
- }
-
- /**
- * Gets foreign relationship constraint keys and table name
- * @param string pgsql foreign key definition
- * @return array foreign relationship table name and keys, null otherwise
- */
- protected function getForeignKeys($src)
- {
- $matches = array();
- $brackets = '\(([^\)]+)\)';
- $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i";
- if(preg_match($find, $src, $matches))
- {
- $keys = preg_split('/,\s+/', $matches[1]);
- $fkeys = array();
- foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey)
- $fkeys[$keys[$i]] = $fkey;
- return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys);
- }
- }
-
- /**
- * @param string column name.
- * @param TPgsqlTableInfo table information.
- * @return boolean true if column is a foreign key.
- */
- protected function isForeignKeyColumn($columnId, $tableInfo)
- {
- foreach($tableInfo->getForeignKeys() as $fk)
- {
- if(in_array($columnId, array_keys($fk['keys'])))
- return true;
- }
- return false;
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Pgsql.TPgsqlTableInfo');
+
+/**
+ * TPgsqlMetaData loads PostgreSQL database table and column information.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ * @since 3.1
+ */
+class TPgsqlMetaData extends TDbMetaData
+{
+ private $_defaultSchema = 'public';
+
+ /**
+ * @return string TDbTableInfo class name.
+ */
+ protected function getTableInfoClass()
+ {
+ return 'TPgsqlTableInfo';
+ }
+
+ /**
+ * Quotes a table name for use in a query.
+ * @param string $name table name
+ * @return string the properly quoted table name
+ */
+ public function quoteTableName($name)
+ {
+ return parent::quoteTableName($name, '"', '"');
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * @param string $name column name
+ * @return string the properly quoted column name
+ */
+ public function quoteColumnName($name)
+ {
+ return parent::quoteColumnName($name, '"', '"');
+ }
+
+ /**
+ * Quotes a column alias for use in a query.
+ * @param string $name column alias
+ * @return string the properly quoted column alias
+ */
+ public function quoteColumnAlias($name)
+ {
+ return parent::quoteColumnAlias($name, '"', '"');
+ }
+
+ /**
+ * @param string default schema.
+ */
+ public function setDefaultSchema($schema)
+ {
+ $this->_defaultSchema=$schema;
+ }
+
+ /**
+ * @return string default schema.
+ */
+ public function getDefaultSchema()
+ {
+ return $this->_defaultSchema;
+ }
+
+ /**
+ * @param string table name with optional schema name prefix, uses default schema name prefix is not provided.
+ * @return array tuple as ($schemaName,$tableName)
+ */
+ protected function getSchemaTableName($table)
+ {
+ if(count($parts= explode('.', str_replace('"','',$table))) > 1)
+ return array($parts[0], $parts[1]);
+ else
+ return array($this->getDefaultSchema(),$parts[0]);
+ }
+
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return TPgsqlTableInfo table information.
+ */
+ protected function createTableInfo($table)
+ {
+ list($schemaName,$tableName) = $this->getSchemaTableName($table);
+
+ // This query is made much more complex by the addition of the 'attisserial' field.
+ // The subquery to get that field checks to see if there is an internally dependent
+ // sequence on the field.
+ $sql =
+<< 0 AND NOT a.attisdropped
+ ORDER BY a.attnum
+EOD;
+ $this->getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ $command->bindValue(':schema', $schemaName);
+ $tableInfo = $this->createNewTableInfo($schemaName, $tableName);
+ $index=0;
+ foreach($command->query() as $col)
+ {
+ $col['index'] = $index++;
+ $this->processColumn($tableInfo, $col);
+ }
+ if($index===0)
+ throw new TDbException('dbmetadata_invalid_table_view', $table);
+ return $tableInfo;
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return TPgsqlTableInfo
+ */
+ protected function createNewTableInfo($schemaName,$tableName)
+ {
+ $info['SchemaName'] = $this->assertIdentifier($schemaName);
+ $info['TableName'] = $this->assertIdentifier($tableName);
+ if($this->getIsView($schemaName,$tableName))
+ $info['IsView'] = true;
+ list($primary, $foreign) = $this->getConstraintKeys($schemaName, $tableName);
+ $class = $this->getTableInfoClass();
+ return new $class($info,$primary,$foreign);
+ }
+
+ /**
+ * @param string table name, schema name or column name.
+ * @return string a valid identifier.
+ * @throws TDbException when table name contains a double quote (").
+ */
+ protected function assertIdentifier($name)
+ {
+ if(strpos($name, '"')!==false)
+ {
+ $ref = 'http://www.postgresql.org/docs/7.4/static/sql-syntax.html#SQL-SYNTAX-IDENTIFIERS';
+ throw new TDbException('dbcommon_invalid_identifier_name', $name, $ref);
+ }
+ return $name;
+ }
+
+ /**
+ * @param string table schema name
+ * @param string table name.
+ * @return boolean true if the table is a view.
+ */
+ protected function getIsView($schemaName,$tableName)
+ {
+ $sql =
+<<getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':schema',$schemaName);
+ $command->bindValue(':table', $tableName);
+ return intval($command->queryScalar()) === 1;
+ }
+
+ /**
+ * @param TPgsqlTableInfo table information.
+ * @param array column information.
+ */
+ protected function processColumn($tableInfo, $col)
+ {
+ $columnId = $col['attname']; //use column name as column Id
+
+ $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
+ $info['ColumnId'] = $columnId;
+ $info['ColumnIndex'] = $col['index'];
+ if(!$col['attnotnull'])
+ $info['AllowNull'] = true;
+ if(in_array($columnId, $tableInfo->getPrimaryKeys()))
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $tableInfo))
+ $info['IsForeignKey'] = true;
+
+ if($col['atttypmod'] > 0)
+ $info['ColumnSize'] = $col['atttypmod'] - 4;
+ if($col['atthasdef'])
+ $info['DefaultValue'] = $col['adsrc'];
+ if($col['attisserial'] || substr($col['adsrc'],0,8) === 'nextval(')
+ {
+ if(($sequence = $this->getSequenceName($tableInfo, $col['adsrc']))!==null)
+ {
+ $info['SequenceName'] = $sequence;
+ unset($info['DefaultValue']);
+ }
+ }
+ $matches = array();
+ if(preg_match('/\((\d+)(?:,(\d+))?+\)/', $col['type'], $matches))
+ {
+ $info['DbType'] = preg_replace('/\(\d+(?:,\d+)?\)/','',$col['type']);
+ if($this->isPrecisionType($info['DbType']))
+ {
+ $info['NumericPrecision'] = intval($matches[1]);
+ if(count($matches) > 2)
+ $info['NumericScale'] = intval($matches[2]);
+ }
+ else
+ $info['ColumnSize'] = intval($matches[1]);
+ }
+ else
+ $info['DbType'] = $col['type'];
+
+ $tableInfo->Columns[$columnId] = new TPgsqlTableColumn($info);
+ }
+
+ /**
+ * @return string serial name if found, null otherwise.
+ */
+ protected function getSequenceName($tableInfo,$src)
+ {
+ $matches = array();
+ if(preg_match('/nextval\([^\']*\'([^\']+)\'[^\)]*\)/i',$src,$matches))
+ {
+ if(is_int(strpos($matches[1], '.')))
+ return $matches[1];
+ else
+ return $tableInfo->getSchemaName().'.'.$matches[1];
+ }
+ }
+
+ /**
+ * @return boolean true if column type if "numeric", "interval" or begins with "time".
+ */
+ protected function isPrecisionType($type)
+ {
+ $type = strtolower(trim($type));
+ return $type==='numeric' || $type==='interval' || strpos($type, 'time')===0;
+ }
+
+ /**
+ * Gets the primary and foreign key column details for the given table.
+ * @param string schema name
+ * @param string table name.
+ * @return array tuple ($primary, $foreign)
+ */
+ protected function getConstraintKeys($schemaName, $tableName)
+ {
+ $sql =
+<<getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ $command->bindValue(':schema', $schemaName);
+ $primary = array();
+ $foreign = array();
+ foreach($command->query() as $row)
+ {
+ switch($row['contype'])
+ {
+ case 'p':
+ $primary = $this->getPrimaryKeys($tableName, $schemaName, $row['indkey']);
+ break;
+ case 'f':
+ if(($fkey = $this->getForeignKeys($row['consrc']))!==null)
+ $foreign[] = $fkey;
+ break;
+ }
+ }
+ return array($primary,$foreign);
+ }
+
+ /**
+ * Gets the primary key field names
+ * @param string pgsql primary key definition
+ * @return array primary key field names.
+ */
+ protected function getPrimaryKeys($tableName, $schemaName, $columnIndex)
+ {
+ $index = join(', ', explode(' ', $columnIndex));
+ $sql =
+<<getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ $command->bindValue(':schema', $schemaName);
+// $command->bindValue(':columnIndex', join(', ', explode(' ', $columnIndex)));
+ $primary = array();
+ foreach($command->query() as $row)
+ {
+ $primary[] = $row['attname'];
+ }
+
+ return $primary;
+ }
+
+ /**
+ * Gets foreign relationship constraint keys and table name
+ * @param string pgsql foreign key definition
+ * @return array foreign relationship table name and keys, null otherwise
+ */
+ protected function getForeignKeys($src)
+ {
+ $matches = array();
+ $brackets = '\(([^\)]+)\)';
+ $find = "/FOREIGN\s+KEY\s+{$brackets}\s+REFERENCES\s+([^\(]+){$brackets}/i";
+ if(preg_match($find, $src, $matches))
+ {
+ $keys = preg_split('/,\s+/', $matches[1]);
+ $fkeys = array();
+ foreach(preg_split('/,\s+/', $matches[3]) as $i => $fkey)
+ $fkeys[$keys[$i]] = $fkey;
+ return array('table' => str_replace('"','',$matches[2]), 'keys' => $fkeys);
+ }
+ }
+
+ /**
+ * @param string column name.
+ * @param TPgsqlTableInfo table information.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $tableInfo)
+ {
+ foreach($tableInfo->getForeignKeys() as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableColumn.php b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php
index 46eab9c1..fe8ff499 100644
--- a/framework/Data/Common/Pgsql/TPgsqlTableColumn.php
+++ b/framework/Data/Common/Pgsql/TPgsqlTableColumn.php
@@ -1,49 +1,49 @@
- * @link http://www.pradosoft.com/
+/**
+ * TPgsqlTableColumn class file.
+ *
+ * @author Wei Zhuo
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a PostgreSQL database table.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlTableColumn extends TDbTableColumn
-{
- private static $types=array(
- 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'),
- 'boolean' => array('boolean'),
- 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric')
- );
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- $dbtype = strtolower($this->getDbType());
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a PostgreSQL database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ * @since 3.1
+ */
+class TPgsqlTableColumn extends TDbTableColumn
+{
+ private static $types=array(
+ 'integer' => array('bit', 'bit varying', 'real', 'serial', 'int', 'integer'),
+ 'boolean' => array('boolean'),
+ 'float' => array('bigint', 'bigserial', 'double precision', 'money', 'numeric')
+ );
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ $dbtype = strtolower($this->getDbType());
+ foreach(self::$types as $type => $dbtypes)
+ {
+ if(in_array($dbtype, $dbtypes))
+ return $type;
+ }
+ return 'string';
+ }
+}
diff --git a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php
index 11e5a613..6ad941d7 100644
--- a/framework/Data/Common/Pgsql/TPgsqlTableInfo.php
+++ b/framework/Data/Common/Pgsql/TPgsqlTableInfo.php
@@ -1,58 +1,58 @@
- * @link http://www.pradosoft.com/
+/**
+ * TPgsqlTableInfo class file.
+ *
+ * @author Wei Zhuo
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common.Pgsql
- */
-
-/**
- * Loads the base TDbTableInfo class and TPgsqlTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn');
-
-/**
- * TPgsqlTableInfo class provides additional table information for PostgreSQL database.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common.Pgsql
- * @since 3.1
- */
-class TPgsqlTableInfo extends TDbTableInfo
-{
- /**
- * @return string name of the schema this column belongs to.
- */
- public function getSchemaName()
- {
- return $this->getInfo('SchemaName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- if(($schema=$this->getSchemaName())!==null)
- return $schema.'.'.$this->getTableName();
- else
- return $this->getTableName();
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Pgsql.TPgsqlCommandBuilder');
- return new TPgsqlCommandBuilder($connection,$this);
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TPgsqlTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Pgsql.TPgsqlTableColumn');
+
+/**
+ * TPgsqlTableInfo class provides additional table information for PostgreSQL database.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common.Pgsql
+ * @since 3.1
+ */
+class TPgsqlTableInfo extends TDbTableInfo
+{
+ /**
+ * @return string name of the schema this column belongs to.
+ */
+ public function getSchemaName()
+ {
+ return $this->getInfo('SchemaName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ if(($schema=$this->getSchemaName())!==null)
+ return $schema.'.'.$this->getTableName();
+ else
+ return $this->getTableName();
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.Pgsql.TPgsqlCommandBuilder');
+ return new TPgsqlCommandBuilder($connection,$this);
+ }
+}
diff --git a/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php
index d162ed60..d7d82812 100644
--- a/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php
+++ b/framework/Data/Common/Sqlite/TSqliteCommandBuilder.php
@@ -1,47 +1,47 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- */
-
-Prado::using('System.Data.Common.TDbCommandBuilder');
-
-/**
- * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands
- * for Sqlite database.
- *
- * @author Wei Zhuo
- * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
- * @package System.Data.Common
- * @since 3.1
- */
-class TSqliteCommandBuilder extends TDbCommandBuilder
-{
- /**
- * Alters the sql to apply $limit and $offset.
- * @param string SQL query string.
- * @param integer maximum number of rows, -1 to ignore limit.
- * @param integer row offset, -1 to ignore offset.
- * @return string SQL with limit and offset.
- */
- public function applyLimitOffset($sql, $limit=-1, $offset=-1)
- {
- $limit = $limit!==null ? intval($limit) : -1;
- $offset = $offset!==null ? intval($offset) : -1;
- if($limit > 0 || $offset > 0)
- {
- $limitStr = ' LIMIT '.$limit;
- $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
- return $sql.$limitStr.$offsetStr;
- }
- else
- return $sql;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ */
+
+Prado::using('System.Data.Common.TDbCommandBuilder');
+
+/**
+ * TSqliteCommandBuilder provides specifics methods to create limit/offset query commands
+ * for Sqlite database.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TDbCommandBuilder.php 1863 2007-04-12 12:43:49Z wei $
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TSqliteCommandBuilder extends TDbCommandBuilder
+{
+ /**
+ * Alters the sql to apply $limit and $offset.
+ * @param string SQL query string.
+ * @param integer maximum number of rows, -1 to ignore limit.
+ * @param integer row offset, -1 to ignore offset.
+ * @return string SQL with limit and offset.
+ */
+ public function applyLimitOffset($sql, $limit=-1, $offset=-1)
+ {
+ $limit = $limit!==null ? intval($limit) : -1;
+ $offset = $offset!==null ? intval($offset) : -1;
+ if($limit > 0 || $offset > 0)
+ {
+ $limitStr = ' LIMIT '.$limit;
+ $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
+ return $sql.$limitStr.$offsetStr;
+ }
+ else
+ return $sql;
+ }
+}
+
diff --git a/framework/Data/Common/Sqlite/TSqliteMetaData.php b/framework/Data/Common/Sqlite/TSqliteMetaData.php
index 5acfad9d..d101d179 100644
--- a/framework/Data/Common/Sqlite/TSqliteMetaData.php
+++ b/framework/Data/Common/Sqlite/TSqliteMetaData.php
@@ -1,210 +1,210 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Load the base TDbMetaData class.
- */
-Prado::using('System.Data.Common.TDbMetaData');
-Prado::using('System.Data.Common.Sqlite.TSqliteTableInfo');
-
-/**
- * TSqliteMetaData loads SQLite database table and column information.
- *
- * @author Wei Zhuo
- * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Commom.Sqlite
- * @since 3.1
- */
-class TSqliteMetaData extends TDbMetaData
-{
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TSqliteTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- return parent::quoteTableName($name, "'", "'");
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- return parent::quoteColumnName($name, '"', '"');
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- return parent::quoteColumnAlias($name, '"', '"');
- }
-
- /**
- * Get the column definitions for given table.
- * @param string table name.
- * @return TPgsqlTableInfo table information.
- */
- protected function createTableInfo($tableName)
- {
- $tableName = str_replace("'",'',$tableName);
- $this->getDbConnection()->setActive(true);
- $table = $this->getDbConnection()->quoteString($tableName);
- $sql = "PRAGMA table_info({$table})";
- $command = $this->getDbConnection()->createCommand($sql);
- $foreign = $this->getForeignKeys($table);
- $index=0;
- $columns=array();
- $primary=array();
- foreach($command->query() as $col)
- {
- $col['index'] = $index++;
- $column = $this->processColumn($col, $foreign);
- $columns[$col['name']] = $column;
- if($column->getIsPrimaryKey())
- $primary[] = $col['name'];
- }
- $info['TableName'] = $tableName;
- if($this->getIsView($tableName))
- $info['IsView'] = true;
- if(count($columns)===0)
- throw new TDbException('dbmetadata_invalid_table_view', $tableName);
- $class = $this->getTableInfoClass();
- $tableInfo = new $class($info,$primary,$foreign);
- $tableInfo->getColumns()->copyFrom($columns);
- return $tableInfo;
- }
-
- /**
- * @param string table name.
- * @return boolean true if the table is a view.
- */
- protected function getIsView($tableName)
- {
- $sql = 'SELECT count(*) FROM sqlite_master WHERE type="view" AND name= :table';
- $this->getDbConnection()->setActive(true);
- $command = $this->getDbConnection()->createCommand($sql);
- $command->bindValue(':table', $tableName);
- return intval($command->queryScalar()) === 1;
- }
-
- /**
- * @param array column information.
- * @param array foreign key details.
- * @return TSqliteTableColumn column details.
- */
- protected function processColumn($col, $foreign)
- {
- $columnId = $col['name']; //use column name as column Id
-
- $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
- $info['ColumnId'] = $columnId;
- $info['ColumnIndex'] = $col['index'];
-
- if($col['notnull']!=='99')
- $info['AllowNull'] = true;
-
- if($col['pk']==='1')
- $info['IsPrimaryKey'] = true;
- if($this->isForeignKeyColumn($columnId, $foreign))
- $info['IsForeignKey'] = true;
-
- if($col['dflt_value']!==null)
- $info['DefaultValue'] = $col['dflt_value'];
-
- $type = strtolower($col['type']);
- $info['AutoIncrement'] = $type==='integer' && $col['pk']==='1';
-
- $info['DbType'] = $type;
- $match=array();
- if(is_int($pos=strpos($type, '(')) && preg_match('/\((.*)\)/', $type, $match))
- {
- $ps = explode(',', $match[1]);
- if(count($ps)===2)
- {
- $info['NumericPrecision'] = intval($ps[0]);
- $info['NumericScale'] = intval($ps[1]);
- }
- else
- $info['ColumnSize']=intval($match[1]);
- $info['DbType'] = substr($type,0,$pos);
- }
-
- return new TSqliteTableColumn($info);
- }
-
- /**
- *
- *
- * @param string quoted table name.
- * @return array foreign key details.
- */
- protected function getForeignKeys($table)
- {
- $sql = "PRAGMA foreign_key_list({$table})";
- $command = $this->getDbConnection()->createCommand($sql);
- $fkeys = array();
- foreach($command->query() as $col)
- {
- $fkeys[$col['table']]['keys'][$col['from']] = $col['to'];
- $fkeys[$col['table']]['table'] = $col['table'];
- }
- return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
- }
-
- /**
- * @param string column name.
- * @param array foreign key column names.
- * @return boolean true if column is a foreign key.
- */
- protected function isForeignKeyColumn($columnId, $foreign)
- {
- foreach($foreign as $fk)
- {
- if(in_array($columnId, array_keys($fk['keys'])))
- return true;
- }
- return false;
- }
-}
-
-/**
-
-CREATE TABLE foo
-(
- id INTEGER NOT NULL PRIMARY KEY,
- id2 CHAR(2)
-);
-
-CREATE TABLE bar
-(
- id INTEGER NOT NULL PRIMARY KEY,
- foo_id INTEGER
- CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE
-);
-*/
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Sqlite
+ */
+
+/**
+ * Load the base TDbMetaData class.
+ */
+Prado::using('System.Data.Common.TDbMetaData');
+Prado::using('System.Data.Common.Sqlite.TSqliteTableInfo');
+
+/**
+ * TSqliteMetaData loads SQLite database table and column information.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TSqliteMetaData.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Commom.Sqlite
+ * @since 3.1
+ */
+class TSqliteMetaData extends TDbMetaData
+{
+ /**
+ * @return string TDbTableInfo class name.
+ */
+ protected function getTableInfoClass()
+ {
+ return 'TSqliteTableInfo';
+ }
+
+ /**
+ * Quotes a table name for use in a query.
+ * @param string $name table name
+ * @return string the properly quoted table name
+ */
+ public function quoteTableName($name)
+ {
+ return parent::quoteTableName($name, "'", "'");
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * @param string $name column name
+ * @return string the properly quoted column name
+ */
+ public function quoteColumnName($name)
+ {
+ return parent::quoteColumnName($name, '"', '"');
+ }
+
+ /**
+ * Quotes a column alias for use in a query.
+ * @param string $name column alias
+ * @return string the properly quoted column alias
+ */
+ public function quoteColumnAlias($name)
+ {
+ return parent::quoteColumnAlias($name, '"', '"');
+ }
+
+ /**
+ * Get the column definitions for given table.
+ * @param string table name.
+ * @return TPgsqlTableInfo table information.
+ */
+ protected function createTableInfo($tableName)
+ {
+ $tableName = str_replace("'",'',$tableName);
+ $this->getDbConnection()->setActive(true);
+ $table = $this->getDbConnection()->quoteString($tableName);
+ $sql = "PRAGMA table_info({$table})";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $foreign = $this->getForeignKeys($table);
+ $index=0;
+ $columns=array();
+ $primary=array();
+ foreach($command->query() as $col)
+ {
+ $col['index'] = $index++;
+ $column = $this->processColumn($col, $foreign);
+ $columns[$col['name']] = $column;
+ if($column->getIsPrimaryKey())
+ $primary[] = $col['name'];
+ }
+ $info['TableName'] = $tableName;
+ if($this->getIsView($tableName))
+ $info['IsView'] = true;
+ if(count($columns)===0)
+ throw new TDbException('dbmetadata_invalid_table_view', $tableName);
+ $class = $this->getTableInfoClass();
+ $tableInfo = new $class($info,$primary,$foreign);
+ $tableInfo->getColumns()->copyFrom($columns);
+ return $tableInfo;
+ }
+
+ /**
+ * @param string table name.
+ * @return boolean true if the table is a view.
+ */
+ protected function getIsView($tableName)
+ {
+ $sql = 'SELECT count(*) FROM sqlite_master WHERE type="view" AND name= :table';
+ $this->getDbConnection()->setActive(true);
+ $command = $this->getDbConnection()->createCommand($sql);
+ $command->bindValue(':table', $tableName);
+ return intval($command->queryScalar()) === 1;
+ }
+
+ /**
+ * @param array column information.
+ * @param array foreign key details.
+ * @return TSqliteTableColumn column details.
+ */
+ protected function processColumn($col, $foreign)
+ {
+ $columnId = $col['name']; //use column name as column Id
+
+ $info['ColumnName'] = '"'.$columnId.'"'; //quote the column names!
+ $info['ColumnId'] = $columnId;
+ $info['ColumnIndex'] = $col['index'];
+
+ if($col['notnull']!=='99')
+ $info['AllowNull'] = true;
+
+ if($col['pk']==='1')
+ $info['IsPrimaryKey'] = true;
+ if($this->isForeignKeyColumn($columnId, $foreign))
+ $info['IsForeignKey'] = true;
+
+ if($col['dflt_value']!==null)
+ $info['DefaultValue'] = $col['dflt_value'];
+
+ $type = strtolower($col['type']);
+ $info['AutoIncrement'] = $type==='integer' && $col['pk']==='1';
+
+ $info['DbType'] = $type;
+ $match=array();
+ if(is_int($pos=strpos($type, '(')) && preg_match('/\((.*)\)/', $type, $match))
+ {
+ $ps = explode(',', $match[1]);
+ if(count($ps)===2)
+ {
+ $info['NumericPrecision'] = intval($ps[0]);
+ $info['NumericScale'] = intval($ps[1]);
+ }
+ else
+ $info['ColumnSize']=intval($match[1]);
+ $info['DbType'] = substr($type,0,$pos);
+ }
+
+ return new TSqliteTableColumn($info);
+ }
+
+ /**
+ *
+ *
+ * @param string quoted table name.
+ * @return array foreign key details.
+ */
+ protected function getForeignKeys($table)
+ {
+ $sql = "PRAGMA foreign_key_list({$table})";
+ $command = $this->getDbConnection()->createCommand($sql);
+ $fkeys = array();
+ foreach($command->query() as $col)
+ {
+ $fkeys[$col['table']]['keys'][$col['from']] = $col['to'];
+ $fkeys[$col['table']]['table'] = $col['table'];
+ }
+ return count($fkeys) > 0 ? array_values($fkeys) : $fkeys;
+ }
+
+ /**
+ * @param string column name.
+ * @param array foreign key column names.
+ * @return boolean true if column is a foreign key.
+ */
+ protected function isForeignKeyColumn($columnId, $foreign)
+ {
+ foreach($foreign as $fk)
+ {
+ if(in_array($columnId, array_keys($fk['keys'])))
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+
+CREATE TABLE foo
+(
+ id INTEGER NOT NULL PRIMARY KEY,
+ id2 CHAR(2)
+);
+
+CREATE TABLE bar
+(
+ id INTEGER NOT NULL PRIMARY KEY,
+ foo_id INTEGER
+ CONSTRAINT fk_foo_id REFERENCES foo(id) ON DELETE CASCADE
+);
+*/
+
diff --git a/framework/Data/Common/Sqlite/TSqliteTableColumn.php b/framework/Data/Common/Sqlite/TSqliteTableColumn.php
index 915debbe..6783e1ad 100644
--- a/framework/Data/Common/Sqlite/TSqliteTableColumn.php
+++ b/framework/Data/Common/Sqlite/TSqliteTableColumn.php
@@ -1,64 +1,64 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Load common TDbTableCommon class.
- */
-Prado::using('System.Data.Common.TDbTableColumn');
-
-/**
- * Describes the column metadata of the schema for a PostgreSQL database table.
- *
- * @author Wei Zhuo
- * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- * @since 3.1
- */
-class TSqliteTableColumn extends TDbTableColumn
-{
- /**
- * @TODO add sqlite types.
- */
- private static $types = array();
-
- /**
- * Overrides parent implementation, returns PHP type from the db type.
- * @return boolean derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- $dbtype = strtolower($this->getDbType());
- foreach(self::$types as $type => $dbtypes)
- {
- if(in_array($dbtype, $dbtypes))
- return $type;
- }
- return 'string';
- }
-
- /**
- * @return boolean true if column will auto-increment when the column value is inserted as null.
- */
- public function getAutoIncrement()
- {
- return $this->getInfo('AutoIncrement', false);
- }
-
- /**
- * @return boolean true if auto increment is true.
- */
- public function hasSequence()
- {
- return $this->getAutoIncrement();
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Sqlite
+ */
+
+/**
+ * Load common TDbTableCommon class.
+ */
+Prado::using('System.Data.Common.TDbTableColumn');
+
+/**
+ * Describes the column metadata of the schema for a PostgreSQL database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TSqliteTableColumn.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Sqlite
+ * @since 3.1
+ */
+class TSqliteTableColumn extends TDbTableColumn
+{
+ /**
+ * @TODO add sqlite types.
+ */
+ private static $types = array();
+
+ /**
+ * Overrides parent implementation, returns PHP type from the db type.
+ * @return boolean derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ $dbtype = strtolower($this->getDbType());
+ foreach(self::$types as $type => $dbtypes)
+ {
+ if(in_array($dbtype, $dbtypes))
+ return $type;
+ }
+ return 'string';
+ }
+
+ /**
+ * @return boolean true if column will auto-increment when the column value is inserted as null.
+ */
+ public function getAutoIncrement()
+ {
+ return $this->getInfo('AutoIncrement', false);
+ }
+
+ /**
+ * @return boolean true if auto increment is true.
+ */
+ public function hasSequence()
+ {
+ return $this->getAutoIncrement();
+ }
+}
+
diff --git a/framework/Data/Common/Sqlite/TSqliteTableInfo.php b/framework/Data/Common/Sqlite/TSqliteTableInfo.php
index fe9ab846..420dce85 100644
--- a/framework/Data/Common/Sqlite/TSqliteTableInfo.php
+++ b/framework/Data/Common/Sqlite/TSqliteTableInfo.php
@@ -1,47 +1,47 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- */
-
-/**
- * Loads the base TDbTableInfo class and TSqliteTableColumn class.
- */
-Prado::using('System.Data.Common.TDbTableInfo');
-Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn');
-
-/**
- * TSqliteTableInfo class provides additional table information for PostgreSQL database.
- *
- * @author Wei Zhuo
- * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
- * @package System.Data.Common.Sqlite
- * @since 3.1
- */
-class TSqliteTableInfo extends TDbTableInfo
-{
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder');
- return new TSqliteCommandBuilder($connection,$this);
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- return "'".$this->getTableName()."'";
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Sqlite
+ */
+
+/**
+ * Loads the base TDbTableInfo class and TSqliteTableColumn class.
+ */
+Prado::using('System.Data.Common.TDbTableInfo');
+Prado::using('System.Data.Common.Sqlite.TSqliteTableColumn');
+
+/**
+ * TSqliteTableInfo class provides additional table information for PostgreSQL database.
+ *
+ * @author Wei Zhuo
+ * @version $Id: TSqliteTableInfo.php 1861 2007-04-12 08:05:03Z wei $
+ * @package System.Data.Common.Sqlite
+ * @since 3.1
+ */
+class TSqliteTableInfo extends TDbTableInfo
+{
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder');
+ return new TSqliteCommandBuilder($connection,$this);
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ return "'".$this->getTableName()."'";
+ }
+}
+
diff --git a/framework/Data/Common/TDbCommandBuilder.php b/framework/Data/Common/TDbCommandBuilder.php
index 212e5f02..ebdede99 100644
--- a/framework/Data/Common/TDbCommandBuilder.php
+++ b/framework/Data/Common/TDbCommandBuilder.php
@@ -1,507 +1,507 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbCommandBuilder provides basic methods to create query commands for tables
- * giving by {@link setTableInfo TableInfo} the property.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbCommandBuilder extends TComponent
-{
- private $_connection;
- private $_tableInfo;
-
- /**
- * @param TDbConnection database connection.
- * @param TDbTableInfo table information.
- */
- public function __construct($connection=null, $tableInfo=null)
- {
- $this->setDbConnection($connection);
- $this->setTableInfo($tableInfo);
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * @param TDbConnection database connection.
- */
- public function setDbConnection($value)
- {
- $this->_connection=$value;
- }
-
- /**
- * @param TDbTableInfo table information.
- */
- public function setTableInfo($value)
- {
- $this->_tableInfo=$value;
- }
-
- /**
- * @param TDbTableInfo table information.
- */
- public function getTableInfo()
- {
- return $this->_tableInfo;
- }
-
- /**
- * Iterate through all the columns and returns the last insert id of the
- * first column that has a sequence or serial.
- * @return mixed last insert id, null if none is found.
- */
- public function getLastInsertID()
- {
- foreach($this->getTableInfo()->getColumns() as $column)
- {
- if($column->hasSequence())
- return $this->getDbConnection()->getLastInsertID($column->getSequenceName());
- }
- }
-
- /**
- * Alters the sql to apply $limit and $offset. Default implementation is applicable
- * for PostgreSQL, MySQL and SQLite.
- * @param string SQL query string.
- * @param integer maximum number of rows, -1 to ignore limit.
- * @param integer row offset, -1 to ignore offset.
- * @return string SQL with limit and offset.
- */
- public function applyLimitOffset($sql, $limit=-1, $offset=-1)
- {
- $limit = $limit!==null ? (int)$limit : -1;
- $offset = $offset!==null ? (int)$offset : -1;
- $limitStr = $limit >= 0 ? ' LIMIT '.$limit : '';
- $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
- return $sql.$limitStr.$offsetStr;
- }
-
- /**
- * @param string SQL string without existing ordering.
- * @param array pairs of column names as key and direction as value.
- * @return string modified SQL applied with ORDER BY.
- */
- public function applyOrdering($sql, $ordering)
- {
- $orders=array();
- foreach($ordering as $name => $direction)
- {
- $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC';
- if(false !== strpos($name, '(') && false !== strpos($name, ')')) {
- // key is a function (bad practice, but we need to handle it)
- $key = $name;
- } else {
- // key is a column
- $key = $this->getTableInfo()->getColumn($name)->getColumnName();
- }
- $orders[] = $key.' '.$direction;
- }
- if(count($orders) > 0)
- $sql .= ' ORDER BY '.implode(', ', $orders);
- return $sql;
- }
-
- /**
- * Computes the SQL condition for search a set of column using regular expression
- * (or LIKE, depending on database implementation) to match a string of
- * keywords (default matches all keywords).
- * @param array list of column id for potential search condition.
- * @param string string of keywords
- * @return string SQL search condition matching on a set of columns.
- */
- public function getSearchExpression($fields, $keywords)
- {
- if(strlen(trim($keywords)) == 0) return '';
- $words = preg_split('/\s/u', $keywords);
- $conditions = array();
- foreach($fields as $field)
- {
- $column = $this->getTableInfo()->getColumn($field)->getColumnName();
- $conditions[] = $this->getSearchCondition($column, $words);
- }
- return '('.implode(' OR ', $conditions).')';
- }
-
- /**
- * @param string column name.
- * @param array keywords
- * @return string search condition for all words in one column.
- */
- protected function getSearchCondition($column, $words)
- {
- $conditions=array();
- foreach($words as $word)
- $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
- return '('.implode(' AND ', $conditions).')';
- }
-
- /**
- *
- * Different behavior depends on type of passed data
- * string
- * usage without modification
- *
- * null
- * will be expanded to full list of quoted table column names (quoting depends on database)
- *
- * array
- * - Column names will be quoted if used as key or value of array
- *
- * array('col1', 'col2', 'col2')
- * // SELECT `col1`, `col2`, `col3` FROM...
- *
- *
- * - Column aliasing
- *
- * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
- * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
- *
- *
- * - NULL and scalar values (strings will be quoted depending on database)
- *
- * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
- * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
- *
- *
- * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
- *
- * array('col1' => 'NULL', '*')
- * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
- *
- * @param mixed $value
- * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage
- * @since 3.1.7
- * @todo add support for table aliasing
- * @todo add support for quoting of column aliasing
- */
- public function getSelectFieldList($data='*') {
- if(is_scalar($data)) {
- $tmp = explode(',', $data);
- $result = array();
- foreach($tmp as $v)
- $result[] = trim($v);
- return $result;
- }
-
- $bHasWildcard = false;
- $result = array();
- if(is_array($data) || $data instanceof Traversable) {
- $columns = $this->getTableInfo()->getColumns();
- foreach($data as $key=>$value) {
- if($key==='*' || $value==='*') {
- $bHasWildcard = true;
- continue;
- }
-
- if(strToUpper($key)==='NULL') {
- $result[] = 'NULL';
- continue;
- }
-
- if(strpos($key, '(')!==false && strpos($key, ')')!==false) {
- $result[] = $key;
- continue;
- }
-
- if(stripos($key, 'AS')!==false) {
- $result[] = $key;
- continue;
- }
-
- if(stripos($value, 'AS')!==false) {
- $result[] = $value;
- continue;
- }
-
- $v = isset($columns[$value]);
- $k = isset($columns[$key]);
- if(is_integer($key) && $v) {
- $key = $value;
- $k = $v;
- }
-
- if(strToUpper($value)==='NULL') {
- if($k)
- $result[] = 'NULL AS ' . $columns[$key]->getColumnName();
- else
- $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : '');
- continue;
- }
-
- if(strpos($value, '(')!==false && strpos($value, ')')!==false) {
- if($k)
- $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
- else
- $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : '');
- continue;
- }
-
- if($v && $key==$value) {
- $result[] = $columns[$value]->getColumnName();
- continue;
- }
-
- if($k && $value==null) {
- $result[] = $columns[$key]->getColumnName();
- continue;
- }
-
- if(is_string($key) && $v) {
- $result[] = $columns[$value]->getColumnName() . ' AS ' . $key;
- continue;
- }
-
- if(is_numeric($value) && $k) {
- $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
- continue;
- }
-
- if(is_string($value) && $k) {
- $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName();
- continue;
- }
-
- if(!$v && !$k && is_integer($key)) {
- $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value);
- continue;
- }
-
- $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key;
- }
- }
-
- if($data===null || count($result) == 0 || $bHasWildcard)
- $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result);
-
- return $result;
- }
-
- /**
- * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
- * The tableName is obtained from the {@link setTableInfo TableInfo} property.
- * @param string query condition
- * @param array condition parameters.
- * @return TDbCommand query command.
- */
- public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*')
- {
- $table = $this->getTableInfo()->getTableFullName();
- $fields = implode(', ', $this -> getSelectFieldList($select));
- $sql = "SELECT {$fields} FROM {$table}";
- if(!empty($where))
- $sql .= " WHERE {$where}";
- return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
- }
-
- public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
- {
- if(count($ordering) > 0)
- $sql = $this->applyOrdering($sql, $ordering);
- if($limit>=0 || $offset>=0)
- $sql = $this->applyLimitOffset($sql, $limit, $offset);
- $command = $this->createCommand($sql);
- $this->bindArrayValues($command, $parameters);
- return $command;
- }
-
- /**
- * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}.
- * @param string count condition.
- * @param array binding parameters.
- * @return TDbCommand count command.
- */
- public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
- {
- return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)');
- }
-
- /**
- * Creates a delete command for the table described in {@link setTableInfo TableInfo}.
- * The conditions for delete is given by the $where argument and the parameters
- * for the condition is given by $parameters.
- * @param string delete condition.
- * @param array delete parameters.
- * @return TDbCommand delete command.
- */
- public function createDeleteCommand($where,$parameters=array())
- {
- $table = $this->getTableInfo()->getTableFullName();
- if (!empty($where))
- $where = ' WHERE '.$where;
- $command = $this->createCommand("DELETE FROM {$table}".$where);
- $this->bindArrayValues($command, $parameters);
- return $command;
- }
-
- /**
- * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data.
- * Each array key in the $data array must correspond to the column name of the table
- * (if a column allows to be null, it may be omitted) to be inserted with
- * the corresponding array value.
- * @param array name-value pairs of new data to be inserted.
- * @return TDbCommand insert command
- */
- public function createInsertCommand($data)
- {
- $table = $this->getTableInfo()->getTableFullName();
- list($fields, $bindings) = $this->getInsertFieldBindings($data);
- $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)");
- $this->bindColumnValues($command, $data);
- return $command;
- }
-
- /**
- * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data.
- * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value.
- * @param array name-value pairs of data to be updated.
- * @param string update condition.
- * @param array update parameters.
- * @return TDbCommand update command.
- */
- public function createUpdateCommand($data, $where, $parameters=array())
- {
- $table = $this->getTableInfo()->getTableFullName();
- if($this->hasIntegerKey($parameters))
- $fields = implode(', ', $this->getColumnBindings($data, true));
- else
- $fields = implode(', ', $this->getColumnBindings($data));
-
- if (!empty($where))
- $where = ' WHERE '.$where;
- $command = $this->createCommand("UPDATE {$table} SET {$fields}".$where);
- $this->bindArrayValues($command, array_merge($data, $parameters));
- return $command;
- }
-
- /**
- * Returns a list of insert field name and a list of binding names.
- * @param object array or object to be inserted.
- * @return array tuple ($fields, $bindings)
- */
- protected function getInsertFieldBindings($values)
- {
- $fields = array(); $bindings=array();
- foreach(array_keys($values) as $name)
- {
- $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName();
- $bindings[] = ':'.$name;
- }
- return array(implode(', ',$fields), implode(', ', $bindings));
- }
-
- /**
- * Create a name-value or position-value if $position=true binding strings.
- * @param array data for binding.
- * @param boolean true to bind as position values.
- * @return string update column names with corresponding binding substrings.
- */
- protected function getColumnBindings($values, $position=false)
- {
- $bindings=array();
- foreach(array_keys($values) as $name)
- {
- $column = $this->getTableInfo()->getColumn($name)->getColumnName();
- $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name;
- }
- return $bindings;
- }
-
- /**
- * @param string SQL query string.
- * @return TDbCommand corresponding database command.
- */
- public function createCommand($sql)
- {
- $this->getDbConnection()->setActive(true);
- return $this->getDbConnection()->createCommand($sql);
- }
-
- /**
- * Bind the name-value pairs of $values where the array keys correspond to column names.
- * @param TDbCommand database command.
- * @param array name-value pairs.
- */
- public function bindColumnValues($command, $values)
- {
- foreach($values as $name=>$value)
- {
- $column = $this->getTableInfo()->getColumn($name);
- if($value === null && $column->getAllowNull())
- $command->bindValue(':'.$name, null, PDO::PARAM_NULL);
- else
- $command->bindValue(':'.$name, $value, $column->getPdoType());
- }
- }
-
- /**
- * @param TDbCommand database command
- * @param array values for binding.
- */
- public function bindArrayValues($command, $values)
- {
- if($this->hasIntegerKey($values))
- {
- $values = array_values($values);
- for($i = 0, $max=count($values); $i<$max; $i++)
- $command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i]));
- }
- else
- {
- foreach($values as $name=>$value)
- {
- $prop = $name[0]===':' ? $name : ':'.$name;
- $command->bindValue($prop, $value, $this->getPdoType($value));
- }
- }
- }
-
- /**
- * @param mixed PHP value
- * @return integer PDO parameter types.
- */
- public static function getPdoType($value)
- {
- switch(gettype($value))
- {
- case 'boolean': return PDO::PARAM_BOOL;
- case 'integer': return PDO::PARAM_INT;
- case 'string' : return PDO::PARAM_STR;
- case 'NULL' : return PDO::PARAM_NULL;
- }
- }
-
- /**
- * @param array
- * @return boolean true if any array key is an integer.
- */
- protected function hasIntegerKey($array)
- {
- foreach($array as $k=>$v)
- {
- if(gettype($k)==='integer')
- return true;
- }
- return false;
- }
-}
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbCommandBuilder provides basic methods to create query commands for tables
+ * giving by {@link setTableInfo TableInfo} the property.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbCommandBuilder extends TComponent
+{
+ private $_connection;
+ private $_tableInfo;
+
+ /**
+ * @param TDbConnection database connection.
+ * @param TDbTableInfo table information.
+ */
+ public function __construct($connection=null, $tableInfo=null)
+ {
+ $this->setDbConnection($connection);
+ $this->setTableInfo($tableInfo);
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ */
+ public function setDbConnection($value)
+ {
+ $this->_connection=$value;
+ }
+
+ /**
+ * @param TDbTableInfo table information.
+ */
+ public function setTableInfo($value)
+ {
+ $this->_tableInfo=$value;
+ }
+
+ /**
+ * @param TDbTableInfo table information.
+ */
+ public function getTableInfo()
+ {
+ return $this->_tableInfo;
+ }
+
+ /**
+ * Iterate through all the columns and returns the last insert id of the
+ * first column that has a sequence or serial.
+ * @return mixed last insert id, null if none is found.
+ */
+ public function getLastInsertID()
+ {
+ foreach($this->getTableInfo()->getColumns() as $column)
+ {
+ if($column->hasSequence())
+ return $this->getDbConnection()->getLastInsertID($column->getSequenceName());
+ }
+ }
+
+ /**
+ * Alters the sql to apply $limit and $offset. Default implementation is applicable
+ * for PostgreSQL, MySQL and SQLite.
+ * @param string SQL query string.
+ * @param integer maximum number of rows, -1 to ignore limit.
+ * @param integer row offset, -1 to ignore offset.
+ * @return string SQL with limit and offset.
+ */
+ public function applyLimitOffset($sql, $limit=-1, $offset=-1)
+ {
+ $limit = $limit!==null ? (int)$limit : -1;
+ $offset = $offset!==null ? (int)$offset : -1;
+ $limitStr = $limit >= 0 ? ' LIMIT '.$limit : '';
+ $offsetStr = $offset >= 0 ? ' OFFSET '.$offset : '';
+ return $sql.$limitStr.$offsetStr;
+ }
+
+ /**
+ * @param string SQL string without existing ordering.
+ * @param array pairs of column names as key and direction as value.
+ * @return string modified SQL applied with ORDER BY.
+ */
+ public function applyOrdering($sql, $ordering)
+ {
+ $orders=array();
+ foreach($ordering as $name => $direction)
+ {
+ $direction = strtolower($direction) == 'desc' ? 'DESC' : 'ASC';
+ if(false !== strpos($name, '(') && false !== strpos($name, ')')) {
+ // key is a function (bad practice, but we need to handle it)
+ $key = $name;
+ } else {
+ // key is a column
+ $key = $this->getTableInfo()->getColumn($name)->getColumnName();
+ }
+ $orders[] = $key.' '.$direction;
+ }
+ if(count($orders) > 0)
+ $sql .= ' ORDER BY '.implode(', ', $orders);
+ return $sql;
+ }
+
+ /**
+ * Computes the SQL condition for search a set of column using regular expression
+ * (or LIKE, depending on database implementation) to match a string of
+ * keywords (default matches all keywords).
+ * @param array list of column id for potential search condition.
+ * @param string string of keywords
+ * @return string SQL search condition matching on a set of columns.
+ */
+ public function getSearchExpression($fields, $keywords)
+ {
+ if(strlen(trim($keywords)) == 0) return '';
+ $words = preg_split('/\s/u', $keywords);
+ $conditions = array();
+ foreach($fields as $field)
+ {
+ $column = $this->getTableInfo()->getColumn($field)->getColumnName();
+ $conditions[] = $this->getSearchCondition($column, $words);
+ }
+ return '('.implode(' OR ', $conditions).')';
+ }
+
+ /**
+ * @param string column name.
+ * @param array keywords
+ * @return string search condition for all words in one column.
+ */
+ protected function getSearchCondition($column, $words)
+ {
+ $conditions=array();
+ foreach($words as $word)
+ $conditions[] = $column.' LIKE '.$this->getDbConnection()->quoteString('%'.$word.'%');
+ return '('.implode(' AND ', $conditions).')';
+ }
+
+ /**
+ *
+ * Different behavior depends on type of passed data
+ * string
+ * usage without modification
+ *
+ * null
+ * will be expanded to full list of quoted table column names (quoting depends on database)
+ *
+ * array
+ * - Column names will be quoted if used as key or value of array
+ *
+ * array('col1', 'col2', 'col2')
+ * // SELECT `col1`, `col2`, `col3` FROM...
+ *
+ *
+ * - Column aliasing
+ *
+ * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
+ * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
+ *
+ *
+ * - NULL and scalar values (strings will be quoted depending on database)
+ *
+ * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
+ * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
+ *
+ *
+ * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
+ *
+ * array('col1' => 'NULL', '*')
+ * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
+ *
+ * @param mixed $value
+ * @return array of generated fields - use implode(', ', $selectfieldlist) to collapse field list for usage
+ * @since 3.1.7
+ * @todo add support for table aliasing
+ * @todo add support for quoting of column aliasing
+ */
+ public function getSelectFieldList($data='*') {
+ if(is_scalar($data)) {
+ $tmp = explode(',', $data);
+ $result = array();
+ foreach($tmp as $v)
+ $result[] = trim($v);
+ return $result;
+ }
+
+ $bHasWildcard = false;
+ $result = array();
+ if(is_array($data) || $data instanceof Traversable) {
+ $columns = $this->getTableInfo()->getColumns();
+ foreach($data as $key=>$value) {
+ if($key==='*' || $value==='*') {
+ $bHasWildcard = true;
+ continue;
+ }
+
+ if(strToUpper($key)==='NULL') {
+ $result[] = 'NULL';
+ continue;
+ }
+
+ if(strpos($key, '(')!==false && strpos($key, ')')!==false) {
+ $result[] = $key;
+ continue;
+ }
+
+ if(stripos($key, 'AS')!==false) {
+ $result[] = $key;
+ continue;
+ }
+
+ if(stripos($value, 'AS')!==false) {
+ $result[] = $value;
+ continue;
+ }
+
+ $v = isset($columns[$value]);
+ $k = isset($columns[$key]);
+ if(is_integer($key) && $v) {
+ $key = $value;
+ $k = $v;
+ }
+
+ if(strToUpper($value)==='NULL') {
+ if($k)
+ $result[] = 'NULL AS ' . $columns[$key]->getColumnName();
+ else
+ $result[] = 'NULL' . (is_string($key) ? (' AS ' . (string)$key) : '');
+ continue;
+ }
+
+ if(strpos($value, '(')!==false && strpos($value, ')')!==false) {
+ if($k)
+ $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
+ else
+ $result[] = $value . (is_string($key) ? (' AS ' . (string)$key) : '');
+ continue;
+ }
+
+ if($v && $key==$value) {
+ $result[] = $columns[$value]->getColumnName();
+ continue;
+ }
+
+ if($k && $value==null) {
+ $result[] = $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(is_string($key) && $v) {
+ $result[] = $columns[$value]->getColumnName() . ' AS ' . $key;
+ continue;
+ }
+
+ if(is_numeric($value) && $k) {
+ $result[] = $value . ' AS ' . $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(is_string($value) && $k) {
+ $result[] = $this->getDbConnection()->quoteString($value) . ' AS ' . $columns[$key]->getColumnName();
+ continue;
+ }
+
+ if(!$v && !$k && is_integer($key)) {
+ $result[] = is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value);
+ continue;
+ }
+
+ $result[] = (is_numeric($value) ? $value : $this->getDbConnection()->quoteString((string)$value)) . ' AS ' . $key;
+ }
+ }
+
+ if($data===null || count($result) == 0 || $bHasWildcard)
+ $result = $result = array_merge($this->getTableInfo()->getColumnNames(), $result);
+
+ return $result;
+ }
+
+ /**
+ * Appends the $where condition to the string "SELECT * FROM tableName WHERE ".
+ * The tableName is obtained from the {@link setTableInfo TableInfo} property.
+ * @param string query condition
+ * @param array condition parameters.
+ * @return TDbCommand query command.
+ */
+ public function createFindCommand($where='1=1', $parameters=array(), $ordering=array(), $limit=-1, $offset=-1, $select='*')
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ $fields = implode(', ', $this -> getSelectFieldList($select));
+ $sql = "SELECT {$fields} FROM {$table}";
+ if(!empty($where))
+ $sql .= " WHERE {$where}";
+ return $this->applyCriterias($sql, $parameters, $ordering, $limit, $offset);
+ }
+
+ public function applyCriterias($sql, $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
+ {
+ if(count($ordering) > 0)
+ $sql = $this->applyOrdering($sql, $ordering);
+ if($limit>=0 || $offset>=0)
+ $sql = $this->applyLimitOffset($sql, $limit, $offset);
+ $command = $this->createCommand($sql);
+ $this->bindArrayValues($command, $parameters);
+ return $command;
+ }
+
+ /**
+ * Creates a count(*) command for the table described in {@link setTableInfo TableInfo}.
+ * @param string count condition.
+ * @param array binding parameters.
+ * @return TDbCommand count command.
+ */
+ public function createCountCommand($where='1=1', $parameters=array(),$ordering=array(), $limit=-1, $offset=-1)
+ {
+ return $this->createFindCommand($where, $parameters, $ordering, $limit, $offset, 'COUNT(*)');
+ }
+
+ /**
+ * Creates a delete command for the table described in {@link setTableInfo TableInfo}.
+ * The conditions for delete is given by the $where argument and the parameters
+ * for the condition is given by $parameters.
+ * @param string delete condition.
+ * @param array delete parameters.
+ * @return TDbCommand delete command.
+ */
+ public function createDeleteCommand($where,$parameters=array())
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ if (!empty($where))
+ $where = ' WHERE '.$where;
+ $command = $this->createCommand("DELETE FROM {$table}".$where);
+ $this->bindArrayValues($command, $parameters);
+ return $command;
+ }
+
+ /**
+ * Creates an insert command for the table described in {@link setTableInfo TableInfo} for the given data.
+ * Each array key in the $data array must correspond to the column name of the table
+ * (if a column allows to be null, it may be omitted) to be inserted with
+ * the corresponding array value.
+ * @param array name-value pairs of new data to be inserted.
+ * @return TDbCommand insert command
+ */
+ public function createInsertCommand($data)
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ list($fields, $bindings) = $this->getInsertFieldBindings($data);
+ $command = $this->createCommand("INSERT INTO {$table}({$fields}) VALUES ($bindings)");
+ $this->bindColumnValues($command, $data);
+ return $command;
+ }
+
+ /**
+ * Creates an update command for the table described in {@link setTableInfo TableInfo} for the given data.
+ * Each array key in the $data array must correspond to the column name to be updated with the corresponding array value.
+ * @param array name-value pairs of data to be updated.
+ * @param string update condition.
+ * @param array update parameters.
+ * @return TDbCommand update command.
+ */
+ public function createUpdateCommand($data, $where, $parameters=array())
+ {
+ $table = $this->getTableInfo()->getTableFullName();
+ if($this->hasIntegerKey($parameters))
+ $fields = implode(', ', $this->getColumnBindings($data, true));
+ else
+ $fields = implode(', ', $this->getColumnBindings($data));
+
+ if (!empty($where))
+ $where = ' WHERE '.$where;
+ $command = $this->createCommand("UPDATE {$table} SET {$fields}".$where);
+ $this->bindArrayValues($command, array_merge($data, $parameters));
+ return $command;
+ }
+
+ /**
+ * Returns a list of insert field name and a list of binding names.
+ * @param object array or object to be inserted.
+ * @return array tuple ($fields, $bindings)
+ */
+ protected function getInsertFieldBindings($values)
+ {
+ $fields = array(); $bindings=array();
+ foreach(array_keys($values) as $name)
+ {
+ $fields[] = $this->getTableInfo()->getColumn($name)->getColumnName();
+ $bindings[] = ':'.$name;
+ }
+ return array(implode(', ',$fields), implode(', ', $bindings));
+ }
+
+ /**
+ * Create a name-value or position-value if $position=true binding strings.
+ * @param array data for binding.
+ * @param boolean true to bind as position values.
+ * @return string update column names with corresponding binding substrings.
+ */
+ protected function getColumnBindings($values, $position=false)
+ {
+ $bindings=array();
+ foreach(array_keys($values) as $name)
+ {
+ $column = $this->getTableInfo()->getColumn($name)->getColumnName();
+ $bindings[] = $position ? $column.' = ?' : $column.' = :'.$name;
+ }
+ return $bindings;
+ }
+
+ /**
+ * @param string SQL query string.
+ * @return TDbCommand corresponding database command.
+ */
+ public function createCommand($sql)
+ {
+ $this->getDbConnection()->setActive(true);
+ return $this->getDbConnection()->createCommand($sql);
+ }
+
+ /**
+ * Bind the name-value pairs of $values where the array keys correspond to column names.
+ * @param TDbCommand database command.
+ * @param array name-value pairs.
+ */
+ public function bindColumnValues($command, $values)
+ {
+ foreach($values as $name=>$value)
+ {
+ $column = $this->getTableInfo()->getColumn($name);
+ if($value === null && $column->getAllowNull())
+ $command->bindValue(':'.$name, null, PDO::PARAM_NULL);
+ else
+ $command->bindValue(':'.$name, $value, $column->getPdoType());
+ }
+ }
+
+ /**
+ * @param TDbCommand database command
+ * @param array values for binding.
+ */
+ public function bindArrayValues($command, $values)
+ {
+ if($this->hasIntegerKey($values))
+ {
+ $values = array_values($values);
+ for($i = 0, $max=count($values); $i<$max; $i++)
+ $command->bindValue($i+1, $values[$i], $this->getPdoType($values[$i]));
+ }
+ else
+ {
+ foreach($values as $name=>$value)
+ {
+ $prop = $name[0]===':' ? $name : ':'.$name;
+ $command->bindValue($prop, $value, $this->getPdoType($value));
+ }
+ }
+ }
+
+ /**
+ * @param mixed PHP value
+ * @return integer PDO parameter types.
+ */
+ public static function getPdoType($value)
+ {
+ switch(gettype($value))
+ {
+ case 'boolean': return PDO::PARAM_BOOL;
+ case 'integer': return PDO::PARAM_INT;
+ case 'string' : return PDO::PARAM_STR;
+ case 'NULL' : return PDO::PARAM_NULL;
+ }
+ }
+
+ /**
+ * @param array
+ * @return boolean true if any array key is an integer.
+ */
+ protected function hasIntegerKey($array)
+ {
+ foreach($array as $k=>$v)
+ {
+ if(gettype($k)==='integer')
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/framework/Data/Common/TDbMetaData.php b/framework/Data/Common/TDbMetaData.php
index fdf367c9..5fc4fec9 100644
--- a/framework/Data/Common/TDbMetaData.php
+++ b/framework/Data/Common/TDbMetaData.php
@@ -1,183 +1,183 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbMetaData is the base class for retrieving metadata information, such as
- * table and columns information, from a database connection.
- *
- * Use the {@link getTableInfo} method to retrieve a table information.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-abstract class TDbMetaData extends TComponent
-{
- private $_tableInfoCache=array();
- private $_connection;
-
- /**
- * @var array
- */
- protected static $delimiterIdentifier = array('[', ']', '"', '`', "'");
-
- /**
- * @param TDbConnection database connection.
- */
- public function __construct($conn)
- {
- $this->_connection=$conn;
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * Obtain database specific TDbMetaData class using the driver name of the database connection.
- * @param TDbConnection database connection.
- * @return TDbMetaData database specific TDbMetaData.
- */
- public static function getInstance($conn)
- {
- $conn->setActive(true); //must be connected before retrieving driver name
- $driver = $conn->getDriverName();
- switch(strtolower($driver))
- {
- case 'pgsql':
- Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData');
- return new TPgsqlMetaData($conn);
- case 'mysqli':
- case 'mysql':
- Prado::using('System.Data.Common.Mysql.TMysqlMetaData');
- return new TMysqlMetaData($conn);
- case 'sqlite': //sqlite 3
- case 'sqlite2': //sqlite 2
- Prado::using('System.Data.Common.Sqlite.TSqliteMetaData');
- return new TSqliteMetaData($conn);
- case 'mssql': // Mssql driver on windows hosts
- case 'dblib': // dblib drivers on linux (and maybe others os) hosts
- Prado::using('System.Data.Common.Mssql.TMssqlMetaData');
- return new TMssqlMetaData($conn);
- case 'oci':
- Prado::using('System.Data.Common.Oracle.TOracleMetaData');
- return new TOracleMetaData($conn);
-// case 'ibm':
-// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
-// return new TIbmDb2MetaData($conn);
- default:
- throw new TDbException('ar_invalid_database_driver',$driver);
- }
- }
-
- /**
- * Obtains table meta data information for the current connection and given table name.
- * @param string table or view name
- * @return TDbTableInfo table information.
- */
- public function getTableInfo($tableName=null)
- {
- $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName;
- if(!isset($this->_tableInfoCache[$key]))
- {
- $class = $this->getTableInfoClass();
- $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName);
- $this->_tableInfoCache[$key] = $tableInfo;
- }
- return $this->_tableInfoCache[$key];
- }
-
- /**
- * Creates a command builder for a given table name.
- * @param string table name.
- * @return TDbCommandBuilder command builder instance for the given table.
- */
- public function createCommandBuilder($tableName=null)
- {
- return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection());
- }
-
- /**
- * This method should be implemented by decendent classes.
- * @return TDbTableInfo driver dependent create builder.
- */
- abstract protected function createTableInfo($tableName);
-
- /**
- * @return string TDbTableInfo class name.
- */
- protected function getTableInfoClass()
- {
- return 'TDbTableInfo';
- }
-
- /**
- * Quotes a table name for use in a query.
- * @param string $name table name
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted table name
- */
- public function quoteTableName($name)
- {
- $name = str_replace(self::$delimiterIdentifier, '', $name);
-
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- if(strpos($name, '.')===false)
- return $lft . $name . $rgt;
- $names=explode('.', $name);
- foreach($names as &$n)
- $n = $lft . $n . $rgt;
- return implode('.', $names);
- }
-
- /**
- * Quotes a column name for use in a query.
- * @param string $name column name
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted column name
- */
- public function quoteColumnName($name)
- {
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
- }
-
- /**
- * Quotes a column alias for use in a query.
- * @param string $name column alias
- * @param string $lft left delimiter
- * @param string $rgt right delimiter
- * @return string the properly quoted column alias
- */
- public function quoteColumnAlias($name)
- {
- $args = func_get_args();
- $rgt = $lft = isset($args[1]) ? $args[1] : '';
- $rgt = isset($args[2]) ? $args[2] : $rgt;
-
- return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbMetaData is the base class for retrieving metadata information, such as
+ * table and columns information, from a database connection.
+ *
+ * Use the {@link getTableInfo} method to retrieve a table information.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+abstract class TDbMetaData extends TComponent
+{
+ private $_tableInfoCache=array();
+ private $_connection;
+
+ /**
+ * @var array
+ */
+ protected static $delimiterIdentifier = array('[', ']', '"', '`', "'");
+
+ /**
+ * @param TDbConnection database connection.
+ */
+ public function __construct($conn)
+ {
+ $this->_connection=$conn;
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * Obtain database specific TDbMetaData class using the driver name of the database connection.
+ * @param TDbConnection database connection.
+ * @return TDbMetaData database specific TDbMetaData.
+ */
+ public static function getInstance($conn)
+ {
+ $conn->setActive(true); //must be connected before retrieving driver name
+ $driver = $conn->getDriverName();
+ switch(strtolower($driver))
+ {
+ case 'pgsql':
+ Prado::using('System.Data.Common.Pgsql.TPgsqlMetaData');
+ return new TPgsqlMetaData($conn);
+ case 'mysqli':
+ case 'mysql':
+ Prado::using('System.Data.Common.Mysql.TMysqlMetaData');
+ return new TMysqlMetaData($conn);
+ case 'sqlite': //sqlite 3
+ case 'sqlite2': //sqlite 2
+ Prado::using('System.Data.Common.Sqlite.TSqliteMetaData');
+ return new TSqliteMetaData($conn);
+ case 'mssql': // Mssql driver on windows hosts
+ case 'dblib': // dblib drivers on linux (and maybe others os) hosts
+ Prado::using('System.Data.Common.Mssql.TMssqlMetaData');
+ return new TMssqlMetaData($conn);
+ case 'oci':
+ Prado::using('System.Data.Common.Oracle.TOracleMetaData');
+ return new TOracleMetaData($conn);
+// case 'ibm':
+// Prado::using('System.Data.Common.IbmDb2.TIbmDb2MetaData');
+// return new TIbmDb2MetaData($conn);
+ default:
+ throw new TDbException('ar_invalid_database_driver',$driver);
+ }
+ }
+
+ /**
+ * Obtains table meta data information for the current connection and given table name.
+ * @param string table or view name
+ * @return TDbTableInfo table information.
+ */
+ public function getTableInfo($tableName=null)
+ {
+ $key = $tableName===null?$this->getDbConnection()->getConnectionString():$tableName;
+ if(!isset($this->_tableInfoCache[$key]))
+ {
+ $class = $this->getTableInfoClass();
+ $tableInfo = $tableName===null ? new $class : $this->createTableInfo($tableName);
+ $this->_tableInfoCache[$key] = $tableInfo;
+ }
+ return $this->_tableInfoCache[$key];
+ }
+
+ /**
+ * Creates a command builder for a given table name.
+ * @param string table name.
+ * @return TDbCommandBuilder command builder instance for the given table.
+ */
+ public function createCommandBuilder($tableName=null)
+ {
+ return $this->getTableInfo($tableName)->createCommandBuilder($this->getDbConnection());
+ }
+
+ /**
+ * This method should be implemented by decendent classes.
+ * @return TDbTableInfo driver dependent create builder.
+ */
+ abstract protected function createTableInfo($tableName);
+
+ /**
+ * @return string TDbTableInfo class name.
+ */
+ protected function getTableInfoClass()
+ {
+ return 'TDbTableInfo';
+ }
+
+ /**
+ * Quotes a table name for use in a query.
+ * @param string $name table name
+ * @param string $lft left delimiter
+ * @param string $rgt right delimiter
+ * @return string the properly quoted table name
+ */
+ public function quoteTableName($name)
+ {
+ $name = str_replace(self::$delimiterIdentifier, '', $name);
+
+ $args = func_get_args();
+ $rgt = $lft = isset($args[1]) ? $args[1] : '';
+ $rgt = isset($args[2]) ? $args[2] : $rgt;
+
+ if(strpos($name, '.')===false)
+ return $lft . $name . $rgt;
+ $names=explode('.', $name);
+ foreach($names as &$n)
+ $n = $lft . $n . $rgt;
+ return implode('.', $names);
+ }
+
+ /**
+ * Quotes a column name for use in a query.
+ * @param string $name column name
+ * @param string $lft left delimiter
+ * @param string $rgt right delimiter
+ * @return string the properly quoted column name
+ */
+ public function quoteColumnName($name)
+ {
+ $args = func_get_args();
+ $rgt = $lft = isset($args[1]) ? $args[1] : '';
+ $rgt = isset($args[2]) ? $args[2] : $rgt;
+
+ return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
+ }
+
+ /**
+ * Quotes a column alias for use in a query.
+ * @param string $name column alias
+ * @param string $lft left delimiter
+ * @param string $rgt right delimiter
+ * @return string the properly quoted column alias
+ */
+ public function quoteColumnAlias($name)
+ {
+ $args = func_get_args();
+ $rgt = $lft = isset($args[1]) ? $args[1] : '';
+ $rgt = isset($args[2]) ? $args[2] : $rgt;
+
+ return $lft . str_replace(self::$delimiterIdentifier, '', $name) . $rgt;
+ }
+}
+
diff --git a/framework/Data/Common/TDbTableColumn.php b/framework/Data/Common/TDbTableColumn.php
index 8a0832d8..3967ec0c 100644
--- a/framework/Data/Common/TDbTableColumn.php
+++ b/framework/Data/Common/TDbTableColumn.php
@@ -1,199 +1,199 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbTableColumn class describes the column meta data of the schema for a database table.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbTableColumn extends TComponent
-{
- const UNDEFINED_VALUE= INF; //use infinity for undefined value
-
- private $_info=array();
-
- /**
- * Sets the table column meta data.
- * @param array table column information.
- */
- public function __construct($columnInfo)
- {
- $this->_info=$columnInfo;
- }
-
- /**
- * @param string information array key name
- * @param mixed default value if information array value is null
- * @return mixed information array value.
- */
- protected function getInfo($name,$default=null)
- {
- return isset($this->_info[$name]) ? $this->_info[$name] : $default;
- }
-
- /**
- * @param string information array key name
- * @param mixed new information array value.
- */
- protected function setInfo($name,$value)
- {
- $this->_info[$name]=$value;
- }
-
- /**
- * Returns the derived PHP primitive type from the db type. Default returns 'string'.
- * @return string derived PHP primitive type from the column db type.
- */
- public function getPHPType()
- {
- return 'string';
- }
-
- /**
- * @param integer PDO bind param/value types, default returns string.
- */
- public function getPdoType()
- {
- switch($this->getPHPType())
- {
- case 'boolean': return PDO::PARAM_BOOL;
- case 'integer': return PDO::PARAM_INT;
- case 'string' : return PDO::PARAM_STR;
- }
- return PDO::PARAM_STR;
- }
-
- /**
- * @return string name of the column in the table (identifier quoted).
- */
- public function getColumnName()
- {
- return $this->getInfo('ColumnName');
- }
-
- /**
- * @return string name of the column with quoted identifier.
- */
- public function getColumnId()
- {
- return $this->getInfo('ColumnId');
- }
-
- /**
- * @return string size of the column.
- */
- public function getColumnSize()
- {
- return $this->getInfo('ColumnSize');
- }
-
- /**
- * @return integer zero-based ordinal position of the column in the table.
- */
- public function getColumnIndex()
- {
- return $this->getInfo('ColumnIndex');
- }
-
- /**
- * @return string column type.
- */
- public function getDbType()
- {
- return $this->getInfo('DbType');
- }
-
- /**
- * @return boolean specifies whether value Null is allowed, default is false.
- */
- public function getAllowNull()
- {
- return $this->getInfo('AllowNull',false);
- }
-
- /**
- * @return mixed default column value if column value was null.
- */
- public function getDefaultValue()
- {
- return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE);
- }
-
- /**
- * @return string precision of the column data, if the data is numeric.
- */
- public function getNumericPrecision()
- {
- return $this->getInfo('NumericPrecision');
- }
-
- /**
- * @return string scale of the column data, if the data is numeric.
- */
- public function getNumericScale()
- {
- return $this->getInfo('NumericScale');
- }
-
- public function getMaxiumNumericConstraint()
- {
- if(($precision=$this->getNumericPrecision())!==null)
- {
- $scale=$this->getNumericScale();
- return $scale===null ? pow(10,$precision) : pow(10,$precision-$scale);
- }
- }
-
- /**
- * @return boolean whether this column is a primary key for the table, default is false.
- */
- public function getIsPrimaryKey()
- {
- return $this->getInfo('IsPrimaryKey',false);
- }
-
- /**
- * @return boolean whether this column is a foreign key, default is false.
- */
- public function getIsForeignKey()
- {
- return $this->getInfo('IsForeignKey',false);
- }
-
- /**
- * @param string sequence name, only applicable if column is a sequence
- */
- public function getSequenceName()
- {
- return $this->getInfo('SequenceName');
- }
-
- /**
- * @return boolean whether the column is a sequence.
- */
- public function hasSequence()
- {
- return $this->getSequenceName()!==null;
- }
-
- /**
- * @return boolean whether this column is excluded from insert and update.
- */
- public function getIsExcluded()
- {
- return false;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbTableColumn class describes the column meta data of the schema for a database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbTableColumn extends TComponent
+{
+ const UNDEFINED_VALUE= INF; //use infinity for undefined value
+
+ private $_info=array();
+
+ /**
+ * Sets the table column meta data.
+ * @param array table column information.
+ */
+ public function __construct($columnInfo)
+ {
+ $this->_info=$columnInfo;
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed default value if information array value is null
+ * @return mixed information array value.
+ */
+ protected function getInfo($name,$default=null)
+ {
+ return isset($this->_info[$name]) ? $this->_info[$name] : $default;
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed new information array value.
+ */
+ protected function setInfo($name,$value)
+ {
+ $this->_info[$name]=$value;
+ }
+
+ /**
+ * Returns the derived PHP primitive type from the db type. Default returns 'string'.
+ * @return string derived PHP primitive type from the column db type.
+ */
+ public function getPHPType()
+ {
+ return 'string';
+ }
+
+ /**
+ * @param integer PDO bind param/value types, default returns string.
+ */
+ public function getPdoType()
+ {
+ switch($this->getPHPType())
+ {
+ case 'boolean': return PDO::PARAM_BOOL;
+ case 'integer': return PDO::PARAM_INT;
+ case 'string' : return PDO::PARAM_STR;
+ }
+ return PDO::PARAM_STR;
+ }
+
+ /**
+ * @return string name of the column in the table (identifier quoted).
+ */
+ public function getColumnName()
+ {
+ return $this->getInfo('ColumnName');
+ }
+
+ /**
+ * @return string name of the column with quoted identifier.
+ */
+ public function getColumnId()
+ {
+ return $this->getInfo('ColumnId');
+ }
+
+ /**
+ * @return string size of the column.
+ */
+ public function getColumnSize()
+ {
+ return $this->getInfo('ColumnSize');
+ }
+
+ /**
+ * @return integer zero-based ordinal position of the column in the table.
+ */
+ public function getColumnIndex()
+ {
+ return $this->getInfo('ColumnIndex');
+ }
+
+ /**
+ * @return string column type.
+ */
+ public function getDbType()
+ {
+ return $this->getInfo('DbType');
+ }
+
+ /**
+ * @return boolean specifies whether value Null is allowed, default is false.
+ */
+ public function getAllowNull()
+ {
+ return $this->getInfo('AllowNull',false);
+ }
+
+ /**
+ * @return mixed default column value if column value was null.
+ */
+ public function getDefaultValue()
+ {
+ return $this->getInfo('DefaultValue', self::UNDEFINED_VALUE);
+ }
+
+ /**
+ * @return string precision of the column data, if the data is numeric.
+ */
+ public function getNumericPrecision()
+ {
+ return $this->getInfo('NumericPrecision');
+ }
+
+ /**
+ * @return string scale of the column data, if the data is numeric.
+ */
+ public function getNumericScale()
+ {
+ return $this->getInfo('NumericScale');
+ }
+
+ public function getMaxiumNumericConstraint()
+ {
+ if(($precision=$this->getNumericPrecision())!==null)
+ {
+ $scale=$this->getNumericScale();
+ return $scale===null ? pow(10,$precision) : pow(10,$precision-$scale);
+ }
+ }
+
+ /**
+ * @return boolean whether this column is a primary key for the table, default is false.
+ */
+ public function getIsPrimaryKey()
+ {
+ return $this->getInfo('IsPrimaryKey',false);
+ }
+
+ /**
+ * @return boolean whether this column is a foreign key, default is false.
+ */
+ public function getIsForeignKey()
+ {
+ return $this->getInfo('IsForeignKey',false);
+ }
+
+ /**
+ * @param string sequence name, only applicable if column is a sequence
+ */
+ public function getSequenceName()
+ {
+ return $this->getInfo('SequenceName');
+ }
+
+ /**
+ * @return boolean whether the column is a sequence.
+ */
+ public function hasSequence()
+ {
+ return $this->getSequenceName()!==null;
+ }
+
+ /**
+ * @return boolean whether this column is excluded from insert and update.
+ */
+ public function getIsExcluded()
+ {
+ return false;
+ }
+}
+
diff --git a/framework/Data/Common/TDbTableInfo.php b/framework/Data/Common/TDbTableInfo.php
index 5342212e..c5c79e5b 100644
--- a/framework/Data/Common/TDbTableInfo.php
+++ b/framework/Data/Common/TDbTableInfo.php
@@ -1,166 +1,166 @@
- * @link http://www.pradosoft.com/
+/**
+ * TDbTableInfo class file.
+ *
+ * @author Wei Zhuo
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.Common
- */
-
-/**
- * TDbTableInfo class describes the meta data of a database table.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.Common
- * @since 3.1
- */
-class TDbTableInfo extends TComponent
-{
- private $_info=array();
-
- private $_primaryKeys;
- private $_foreignKeys;
-
- private $_columns;
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.Common
+ */
+
+/**
+ * TDbTableInfo class describes the meta data of a database table.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.Common
+ * @since 3.1
+ */
+class TDbTableInfo extends TComponent
+{
+ private $_info=array();
+
+ private $_primaryKeys;
+ private $_foreignKeys;
+
+ private $_columns;
+
private $_lowercase;
/**
* @var null|array
* @since 3.1.7
- */
- private $_names = null;
-
- /**
- * Sets the database table meta data information.
- * @param array table column information.
- */
- public function __construct($tableInfo=array(),$primary=array(),$foreign=array())
- {
- $this->_info=$tableInfo;
- $this->_primaryKeys=$primary;
- $this->_foreignKeys=$foreign;
- $this->_columns=new TMap;
- }
-
- /**
- * @param TDbConnection database connection.
- * @return TDbCommandBuilder new command builder
- */
- public function createCommandBuilder($connection)
- {
- Prado::using('System.Data.Common.TDbCommandBuilder');
- return new TDbCommandBuilder($connection,$this);
- }
-
- /**
- * @param string information array key name
- * @param mixed default value if information array value is null
- * @return mixed information array value.
- */
- protected function getInfo($name,$default=null)
- {
- return isset($this->_info[$name]) ? $this->_info[$name] : $default;
- }
-
- /**
- * @param string information array key name
- * @param mixed new information array value.
- */
- protected function setInfo($name,$value)
- {
- $this->_info[$name]=$value;
- }
-
- /**
- * @return string name of the table this column belongs to.
- */
- public function getTableName()
- {
- return $this->getInfo('TableName');
- }
-
- /**
- * @return string full name of the table, database dependent.
- */
- public function getTableFullName()
- {
- return $this->getTableName();
- }
-
- /**
- * @return boolean whether the table is a view, default is false.
- */
- public function getIsView()
- {
- return $this->getInfo('IsView',false);
- }
-
- /**
- * @return TMap TDbTableColumn column meta data.
- */
- public function getColumns()
- {
- return $this->_columns;
- }
-
- /**
- * @param string column id
- * @return TDbTableColumn column information.
- */
- public function getColumn($name)
- {
- if(($column = $this->_columns->itemAt($name))!==null)
- return $column;
- throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName());
- }
-
- /**
- * @param array list of column Id, empty to get all columns.
- * @return array table column names (identifier quoted)
- */
- public function getColumnNames()
+ */
+ private $_names = null;
+
+ /**
+ * Sets the database table meta data information.
+ * @param array table column information.
+ */
+ public function __construct($tableInfo=array(),$primary=array(),$foreign=array())
+ {
+ $this->_info=$tableInfo;
+ $this->_primaryKeys=$primary;
+ $this->_foreignKeys=$foreign;
+ $this->_columns=new TMap;
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @return TDbCommandBuilder new command builder
+ */
+ public function createCommandBuilder($connection)
+ {
+ Prado::using('System.Data.Common.TDbCommandBuilder');
+ return new TDbCommandBuilder($connection,$this);
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed default value if information array value is null
+ * @return mixed information array value.
+ */
+ protected function getInfo($name,$default=null)
+ {
+ return isset($this->_info[$name]) ? $this->_info[$name] : $default;
+ }
+
+ /**
+ * @param string information array key name
+ * @param mixed new information array value.
+ */
+ protected function setInfo($name,$value)
+ {
+ $this->_info[$name]=$value;
+ }
+
+ /**
+ * @return string name of the table this column belongs to.
+ */
+ public function getTableName()
+ {
+ return $this->getInfo('TableName');
+ }
+
+ /**
+ * @return string full name of the table, database dependent.
+ */
+ public function getTableFullName()
+ {
+ return $this->getTableName();
+ }
+
+ /**
+ * @return boolean whether the table is a view, default is false.
+ */
+ public function getIsView()
+ {
+ return $this->getInfo('IsView',false);
+ }
+
+ /**
+ * @return TMap TDbTableColumn column meta data.
+ */
+ public function getColumns()
+ {
+ return $this->_columns;
+ }
+
+ /**
+ * @param string column id
+ * @return TDbTableColumn column information.
+ */
+ public function getColumn($name)
+ {
+ if(($column = $this->_columns->itemAt($name))!==null)
+ return $column;
+ throw new TDbException('dbtableinfo_invalid_column_name', $name, $this->getTableFullName());
+ }
+
+ /**
+ * @param array list of column Id, empty to get all columns.
+ * @return array table column names (identifier quoted)
+ */
+ public function getColumnNames()
{
if($this->_names===null)
- {
- $this->_names=array();
- foreach($this->getColumns() as $column)
+ {
+ $this->_names=array();
+ foreach($this->getColumns() as $column)
$this->_names[] = $column->getColumnName();
- }
- return $this->_names;
- }
-
- /**
- * @return string[] names of primary key columns.
- */
- public function getPrimaryKeys()
- {
- return $this->_primaryKeys;
- }
-
- /**
- * @return array tuples of foreign table and column name.
- */
- public function getForeignKeys()
- {
- return $this->_foreignKeys;
- }
-
- /**
- * @return array lowercased column key names mapped to normal column ids.
- */
- public function getLowerCaseColumnNames()
- {
- if($this->_lowercase===null)
- {
- $this->_lowercase=array();
- foreach($this->getColumns()->getKeys() as $key)
- $this->_lowercase[strtolower($key)] = $key;
- }
- return $this->_lowercase;
- }
+ }
+ return $this->_names;
+ }
+
+ /**
+ * @return string[] names of primary key columns.
+ */
+ public function getPrimaryKeys()
+ {
+ return $this->_primaryKeys;
+ }
+
+ /**
+ * @return array tuples of foreign table and column name.
+ */
+ public function getForeignKeys()
+ {
+ return $this->_foreignKeys;
+ }
+
+ /**
+ * @return array lowercased column key names mapped to normal column ids.
+ */
+ public function getLowerCaseColumnNames()
+ {
+ if($this->_lowercase===null)
+ {
+ $this->_lowercase=array();
+ foreach($this->getColumns()->getKeys() as $key)
+ $this->_lowercase[strtolower($key)] = $key;
+ }
+ return $this->_lowercase;
+ }
}
\ No newline at end of file
diff --git a/framework/Data/DataGateway/TDataGatewayCommand.php b/framework/Data/DataGateway/TDataGatewayCommand.php
index fa699979..299d39cd 100644
--- a/framework/Data/DataGateway/TDataGatewayCommand.php
+++ b/framework/Data/DataGateway/TDataGatewayCommand.php
@@ -1,541 +1,541 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.DataGateway
- */
-
-/**
- * TDataGatewayCommand is command builder and executor class for
- * TTableGateway and TActiveRecordGateway.
- *
- * TDataGatewayCommand builds the TDbCommand for TTableGateway
- * and TActiveRecordGateway commands such as find(), update(), insert(), etc,
- * using the TDbCommandBuilder classes (database specific TDbCommandBuilder
- * classes are used).
- *
- * Once the command is built and the query parameters are binded, the
- * {@link OnCreateCommand} event is raised. Event handlers for the OnCreateCommand
- * event should not alter the Command property nor the Criteria property of the
- * TDataGatewayEventParameter.
- *
- * TDataGatewayCommand excutes the TDbCommands and returns the result obtained from the
- * database (returned value depends on the method executed). The
- * {@link OnExecuteCommand} event is raised after the command is executed and resulting
- * data is set in the TDataGatewayResultEventParameter object's Result property.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayCommand extends TComponent
-{
- private $_builder;
-
- /**
- * @param TDbCommandBuilder database specific database command builder.
- */
- public function __construct($builder)
- {
- $this->_builder = $builder;
- }
-
- /**
- * @return TDbTableInfo
- */
- public function getTableInfo()
- {
- return $this->_builder->getTableInfo();
- }
-
- /**
- * @return TDbConnection
- */
- public function getDbConnection()
- {
- return $this->_builder->getDbConnection();
- }
-
- /**
- * @return TDbCommandBuilder
- */
- public function getBuilder()
- {
- return $this->_builder;
- }
-
- /**
- * Executes a delete command.
- * @param TSqlCriteria delete conditions and parameters.
- * @return integer number of records affected.
- */
- public function delete($criteria)
- {
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $command = $this->getBuilder()->createDeleteCommand($where, $parameters);
- $this->onCreateCommand($command,$criteria);
- $command->prepare();
- return $command->execute();
- }
-
- /**
- * Updates the table with new data.
- * @param array date for update.
- * @param TSqlCriteria update conditions and parameters.
- * @return integer number of records affected.
- */
- public function update($data, $criteria)
- {
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $command = $this->getBuilder()->createUpdateCommand($data,$where, $parameters);
- $this->onCreateCommand($command,$criteria);
- $command->prepare();
- return $this->onExecuteCommand($command, $command->execute());
- }
-
- /**
- * @param array update for update
- * @param array primary key-value name pairs.
- * @return integer number of records affected.
- */
- public function updateByPk($data, $keys)
- {
- list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
- return $this->update($data, new TSqlCriteria($where, $parameters));
- }
-
- /**
- * Find one record matching the critera.
- * @param TSqlCriteria find conditions and parameters.
- * @return array matching record.
- */
- public function find($criteria)
- {
- $command = $this->getFindCommand($criteria);
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * Find one or more matching records.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findAll($criteria)
- {
- $command = $this->getFindCommand($criteria);
- return $this->onExecuteCommand($command, $command->query());
- }
-
- /**
- * Build the find command from the criteria. Limit, Offset and Ordering are applied if applicable.
- * @param TSqlCriteria $criteria
- * @return TDbCommand.
- */
- protected function getFindCommand($criteria)
- {
- if($criteria===null)
- return $this->getBuilder()->createFindCommand();
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- $select = $criteria->getSelect();
- $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select);
- $this->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param mixed primary key value, or composite key values as array.
- * @return array matching record.
- */
- public function findByPk($keys)
- {
- list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
- $command = $this->getBuilder()->createFindCommand($where, $parameters);
- $this->onCreateCommand($command, new TSqlCriteria($where,$parameters));
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * @param array multiple primary key values or composite value arrays
- * @return TDbDataReader record reader.
- */
- public function findAllByPk($keys)
- {
- $where = $this->getCompositeKeyCondition((array)$keys);
- $command = $this->getBuilder()->createFindCommand($where);
- $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
- return $this->onExecuteCommand($command,$command->query());
- }
-
- public function findAllByIndex($criteria,$fields,$values)
- {
- $index = $this->getIndexKeyCondition($this->getTableInfo(),$fields,$values);
- if(strlen($where = $criteria->getCondition())>0)
- $criteria->setCondition("({$index}) AND ({$where})");
- else
- $criteria->setCondition($index);
- $command = $this->getFindCommand($criteria);
- $this->onCreateCommand($command, $criteria);
- return $this->onExecuteCommand($command,$command->query());
- }
-
- /**
- * @param array multiple primary key values or composite value arrays
- * @return integer number of rows affected.
- */
- public function deleteByPk($keys)
- {
- $where = $this->getCompositeKeyCondition((array)$keys);
- $command = $this->getBuilder()->createDeleteCommand($where);
- $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
- $command->prepare();
- return $this->onExecuteCommand($command,$command->execute());
- }
-
- public function getIndexKeyCondition($table,$fields,$values)
- {
- if (!count($values))
- return 'FALSE';
- $columns = array();
- $tableName = $table->getTableFullName();
- foreach($fields as $field)
- $columns[] = $tableName.'.'.$table->getColumn($field)->getColumnName();
- return '('.implode(', ',$columns).') IN '.$this->quoteTuple($values);
- }
-
- /**
- * Construct a "pk IN ('key1', 'key2', ...)" criteria.
- * @param array values for IN predicate
- * @param string SQL string for primary keys IN a list.
- */
- protected function getCompositeKeyCondition($values)
- {
- $primary = $this->getTableInfo()->getPrimaryKeys();
- $count = count($primary);
- if($count===0)
- {
- throw new TDbException('dbtablegateway_no_primary_key_found',
- $this->getTableInfo()->getTableFullName());
- }
- if(!is_array($values) || count($values) === 0)
- {
- throw new TDbException('dbtablegateway_missing_pk_values',
- $this->getTableInfo()->getTableFullName());
- }
- if($count>1 && (!isset($values[0]) || !is_array($values[0])))
- $values = array($values);
- if($count > 1 && count($values[0]) !== $count)
- {
- throw new TDbException('dbtablegateway_pk_value_count_mismatch',
- $this->getTableInfo()->getTableFullName());
- }
- return $this->getIndexKeyCondition($this->getTableInfo(),$primary, $values);
- }
-
- /**
- * @param TDbConnection database connection.
- * @param array values
- * @return string quoted recursive tuple values, e.g. "('val1', 'val2')".
- */
- protected function quoteTuple($array)
- {
- $conn = $this->getDbConnection();
- $data = array();
- foreach($array as $k=>$v)
- $data[] = is_array($v) ? $this->quoteTuple($v) : $conn->quoteString($v);
- return '('.implode(', ', $data).')';
- }
-
- /**
- * Create the condition and parameters for find by primary.
- * @param array primary key values
- * @return array tuple($where, $parameters)
- */
- protected function getPrimaryKeyCondition($values)
- {
- $primary = $this->getTableInfo()->getPrimaryKeys();
- if(count($primary)===0)
- {
- throw new TDbException('dbtablegateway_no_primary_key_found',
- $this->getTableInfo()->getTableFullName());
- }
- $criteria=array();
- $bindings=array();
- $i = 0;
- foreach($primary as $key)
- {
- $column = $this->getTableInfo()->getColumn($key)->getColumnName();
- $criteria[] = $column.' = :'.$key;
- $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++];
- }
- return array(implode(' AND ', $criteria), $bindings);
- }
-
- /**
- * Find one matching records for arbituary SQL.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findBySql($criteria)
- {
- $command = $this->getSqlCommand($criteria);
- return $this->onExecuteCommand($command, $command->queryRow());
- }
-
- /**
- * Find zero or more matching records for arbituary SQL.
- * @param TSqlCriteria $criteria
- * @return TDbDataReader record reader.
- */
- public function findAllBySql($criteria)
- {
- $command = $this->getSqlCommand($criteria);
- return $this->onExecuteCommand($command, $command->query());
- }
-
- /**
- * Build sql command from the criteria. Limit, Offset and Ordering are applied if applicable.
- * @param TSqlCriteria $criteria
- * @return TDbCommand command corresponding to the criteria.
- */
- protected function getSqlCommand($criteria)
- {
- $sql = $criteria->getCondition();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- if(count($ordering) > 0)
- $sql = $this->getBuilder()->applyOrdering($sql, $ordering);
- if($limit>=0 || $offset>=0)
- $sql = $this->getBuilder()->applyLimitOffset($sql, $limit, $offset);
- $command = $this->getBuilder()->createCommand($sql);
- $this->getBuilder()->bindArrayValues($command, $criteria->getParameters()->toArray());
- $this->onCreateCommand($command, $criteria);
- return $command;
- }
-
- /**
- * @param TSqlCriteria $criteria
- * @return integer number of records.
- */
- public function count($criteria)
- {
- if($criteria===null)
- return (int)$this->getBuilder()->createCountCommand()->queryScalar();
- $where = $criteria->getCondition();
- $parameters = $criteria->getParameters()->toArray();
- $ordering = $criteria->getOrdersBy();
- $limit = $criteria->getLimit();
- $offset = $criteria->getOffset();
- $command = $this->getBuilder()->createCountCommand($where,$parameters,$ordering,$limit,$offset);
- $this->onCreateCommand($command, $criteria);
- return $this->onExecuteCommand($command, (int)$command->queryScalar());
- }
-
- /**
- * Inserts a new record into the table. Each array key must
- * correspond to a column name in the table unless a null value is permitted.
- * @param array new record data.
- * @return mixed last insert id if one column contains a serial or sequence,
- * otherwise true if command executes successfully and affected 1 or more rows.
- */
- public function insert($data)
- {
- $command=$this->getBuilder()->createInsertCommand($data);
- $this->onCreateCommand($command, new TSqlCriteria(null,$data));
- $command->prepare();
- if($this->onExecuteCommand($command, $command->execute()) > 0)
- {
- $value = $this->getLastInsertId();
- return $value !== null ? $value : true;
- }
- return false;
- }
-
- /**
- * Iterate through all the columns and returns the last insert id of the
- * first column that has a sequence or serial.
- * @return mixed last insert id, null if none is found.
- */
- public function getLastInsertID()
- {
- return $this->getBuilder()->getLastInsertID();
- }
-
- /**
- * @param string __call method name
- * @param string criteria conditions
- * @param array method arguments
- * @return TActiveRecordCriteria criteria created from the method name and its arguments.
- */
- public function createCriteriaFromString($method, $condition, $args)
- {
- $fields = $this->extractMatchingConditions($method, $condition);
- $args=count($args) === 1 && is_array($args[0]) ? $args[0] : $args;
- if(count($fields)>count($args))
- {
- throw new TDbException('dbtablegateway_mismatch_args_exception',
- $method,count($fields),count($args));
- }
- return new TSqlCriteria(implode(' ',$fields), $args);
- }
-
- /**
- * Calculates the AND/OR condition from dynamic method substrings using
- * table meta data, allows for any AND-OR combinations.
- * @param string dynamic method name
- * @param string dynamic method search criteria
- * @return array search condition substrings
- */
- protected function extractMatchingConditions($method, $condition)
- {
- $table = $this->getTableInfo();
- $columns = $table->getLowerCaseColumnNames();
- $regexp = '/('.implode('|', array_keys($columns)).')(and|_and_|or|_or_)?/i';
- $matches = array();
- if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER))
- {
- throw new TDbException('dbtablegateway_mismatch_column_name',
- $method, implode(', ', $columns), $table->getTableFullName());
- }
-
- $fields = array();
- foreach($matches as $match)
- {
- $key = $columns[$match[1]];
- $column = $table->getColumn($key)->getColumnName();
- $sql = $column . ' = ? ';
- if(count($match) > 2)
- $sql .= strtoupper(str_replace('_', '', $match[2]));
- $fields[] = $sql;
- }
- return $fields;
- }
-
- /**
- * Raised when a command is prepared and parameter binding is completed.
- * The parameter object is TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayEventParameter
- */
- public function onCreateCommand($command, $criteria)
- {
- $this->raiseEvent('OnCreateCommand', $this, new TDataGatewayEventParameter($command,$criteria));
- }
-
- /**
- * Raised when a command is executed and the result from the database was returned.
- * The parameter object is TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayResultEventParameter
- */
- public function onExecuteCommand($command, $result)
- {
- $parameter = new TDataGatewayResultEventParameter($command, $result);
- $this->raiseEvent('OnExecuteCommand', $this, $parameter);
- return $parameter->getResult();
- }
-}
-
-/**
- * TDataGatewayEventParameter class contains the TDbCommand to be executed as
- * well as the criteria object.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayEventParameter extends TEventParameter
-{
- private $_command;
- private $_criteria;
-
- public function __construct($command,$criteria)
- {
- $this->_command=$command;
- $this->_criteria=$criteria;
- }
-
- /**
- * The database command to be executed. Do not rebind the parameters or change
- * the sql query string.
- * @return TDbCommand command to be executed.
- */
- public function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return TSqlCriteria criteria used to bind the sql query parameters.
- */
- public function getCriteria()
- {
- return $this->_criteria;
- }
-}
-
-/**
- * TDataGatewayResultEventParameter contains the TDbCommand executed and the resulting
- * data returned from the database. The data can be changed by changing the
- * {@link setResult Result} property.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TDataGatewayResultEventParameter extends TEventParameter
-{
- private $_command;
- private $_result;
-
- public function __construct($command,$result)
- {
- $this->_command=$command;
- $this->_result=$result;
- }
-
- /**
- * @return TDbCommand database command executed.
- */
- public function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return mixed result returned from executing the command.
- */
- public function getResult()
- {
- return $this->_result;
- }
-
- /**
- * @param mixed change the result returned by the gateway.
- */
- public function setResult($value)
- {
- $this->_result=$value;
- }
-}
-
-?>
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.DataGateway
+ */
+
+/**
+ * TDataGatewayCommand is command builder and executor class for
+ * TTableGateway and TActiveRecordGateway.
+ *
+ * TDataGatewayCommand builds the TDbCommand for TTableGateway
+ * and TActiveRecordGateway commands such as find(), update(), insert(), etc,
+ * using the TDbCommandBuilder classes (database specific TDbCommandBuilder
+ * classes are used).
+ *
+ * Once the command is built and the query parameters are binded, the
+ * {@link OnCreateCommand} event is raised. Event handlers for the OnCreateCommand
+ * event should not alter the Command property nor the Criteria property of the
+ * TDataGatewayEventParameter.
+ *
+ * TDataGatewayCommand excutes the TDbCommands and returns the result obtained from the
+ * database (returned value depends on the method executed). The
+ * {@link OnExecuteCommand} event is raised after the command is executed and resulting
+ * data is set in the TDataGatewayResultEventParameter object's Result property.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.DataGateway
+ * @since 3.1
+ */
+class TDataGatewayCommand extends TComponent
+{
+ private $_builder;
+
+ /**
+ * @param TDbCommandBuilder database specific database command builder.
+ */
+ public function __construct($builder)
+ {
+ $this->_builder = $builder;
+ }
+
+ /**
+ * @return TDbTableInfo
+ */
+ public function getTableInfo()
+ {
+ return $this->_builder->getTableInfo();
+ }
+
+ /**
+ * @return TDbConnection
+ */
+ public function getDbConnection()
+ {
+ return $this->_builder->getDbConnection();
+ }
+
+ /**
+ * @return TDbCommandBuilder
+ */
+ public function getBuilder()
+ {
+ return $this->_builder;
+ }
+
+ /**
+ * Executes a delete command.
+ * @param TSqlCriteria delete conditions and parameters.
+ * @return integer number of records affected.
+ */
+ public function delete($criteria)
+ {
+ $where = $criteria->getCondition();
+ $parameters = $criteria->getParameters()->toArray();
+ $command = $this->getBuilder()->createDeleteCommand($where, $parameters);
+ $this->onCreateCommand($command,$criteria);
+ $command->prepare();
+ return $command->execute();
+ }
+
+ /**
+ * Updates the table with new data.
+ * @param array date for update.
+ * @param TSqlCriteria update conditions and parameters.
+ * @return integer number of records affected.
+ */
+ public function update($data, $criteria)
+ {
+ $where = $criteria->getCondition();
+ $parameters = $criteria->getParameters()->toArray();
+ $command = $this->getBuilder()->createUpdateCommand($data,$where, $parameters);
+ $this->onCreateCommand($command,$criteria);
+ $command->prepare();
+ return $this->onExecuteCommand($command, $command->execute());
+ }
+
+ /**
+ * @param array update for update
+ * @param array primary key-value name pairs.
+ * @return integer number of records affected.
+ */
+ public function updateByPk($data, $keys)
+ {
+ list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
+ return $this->update($data, new TSqlCriteria($where, $parameters));
+ }
+
+ /**
+ * Find one record matching the critera.
+ * @param TSqlCriteria find conditions and parameters.
+ * @return array matching record.
+ */
+ public function find($criteria)
+ {
+ $command = $this->getFindCommand($criteria);
+ return $this->onExecuteCommand($command, $command->queryRow());
+ }
+
+ /**
+ * Find one or more matching records.
+ * @param TSqlCriteria $criteria
+ * @return TDbDataReader record reader.
+ */
+ public function findAll($criteria)
+ {
+ $command = $this->getFindCommand($criteria);
+ return $this->onExecuteCommand($command, $command->query());
+ }
+
+ /**
+ * Build the find command from the criteria. Limit, Offset and Ordering are applied if applicable.
+ * @param TSqlCriteria $criteria
+ * @return TDbCommand.
+ */
+ protected function getFindCommand($criteria)
+ {
+ if($criteria===null)
+ return $this->getBuilder()->createFindCommand();
+ $where = $criteria->getCondition();
+ $parameters = $criteria->getParameters()->toArray();
+ $ordering = $criteria->getOrdersBy();
+ $limit = $criteria->getLimit();
+ $offset = $criteria->getOffset();
+ $select = $criteria->getSelect();
+ $command = $this->getBuilder()->createFindCommand($where,$parameters,$ordering,$limit,$offset,$select);
+ $this->onCreateCommand($command, $criteria);
+ return $command;
+ }
+
+ /**
+ * @param mixed primary key value, or composite key values as array.
+ * @return array matching record.
+ */
+ public function findByPk($keys)
+ {
+ list($where, $parameters) = $this->getPrimaryKeyCondition((array)$keys);
+ $command = $this->getBuilder()->createFindCommand($where, $parameters);
+ $this->onCreateCommand($command, new TSqlCriteria($where,$parameters));
+ return $this->onExecuteCommand($command, $command->queryRow());
+ }
+
+ /**
+ * @param array multiple primary key values or composite value arrays
+ * @return TDbDataReader record reader.
+ */
+ public function findAllByPk($keys)
+ {
+ $where = $this->getCompositeKeyCondition((array)$keys);
+ $command = $this->getBuilder()->createFindCommand($where);
+ $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
+ return $this->onExecuteCommand($command,$command->query());
+ }
+
+ public function findAllByIndex($criteria,$fields,$values)
+ {
+ $index = $this->getIndexKeyCondition($this->getTableInfo(),$fields,$values);
+ if(strlen($where = $criteria->getCondition())>0)
+ $criteria->setCondition("({$index}) AND ({$where})");
+ else
+ $criteria->setCondition($index);
+ $command = $this->getFindCommand($criteria);
+ $this->onCreateCommand($command, $criteria);
+ return $this->onExecuteCommand($command,$command->query());
+ }
+
+ /**
+ * @param array multiple primary key values or composite value arrays
+ * @return integer number of rows affected.
+ */
+ public function deleteByPk($keys)
+ {
+ $where = $this->getCompositeKeyCondition((array)$keys);
+ $command = $this->getBuilder()->createDeleteCommand($where);
+ $this->onCreateCommand($command, new TSqlCriteria($where,$keys));
+ $command->prepare();
+ return $this->onExecuteCommand($command,$command->execute());
+ }
+
+ public function getIndexKeyCondition($table,$fields,$values)
+ {
+ if (!count($values))
+ return 'FALSE';
+ $columns = array();
+ $tableName = $table->getTableFullName();
+ foreach($fields as $field)
+ $columns[] = $tableName.'.'.$table->getColumn($field)->getColumnName();
+ return '('.implode(', ',$columns).') IN '.$this->quoteTuple($values);
+ }
+
+ /**
+ * Construct a "pk IN ('key1', 'key2', ...)" criteria.
+ * @param array values for IN predicate
+ * @param string SQL string for primary keys IN a list.
+ */
+ protected function getCompositeKeyCondition($values)
+ {
+ $primary = $this->getTableInfo()->getPrimaryKeys();
+ $count = count($primary);
+ if($count===0)
+ {
+ throw new TDbException('dbtablegateway_no_primary_key_found',
+ $this->getTableInfo()->getTableFullName());
+ }
+ if(!is_array($values) || count($values) === 0)
+ {
+ throw new TDbException('dbtablegateway_missing_pk_values',
+ $this->getTableInfo()->getTableFullName());
+ }
+ if($count>1 && (!isset($values[0]) || !is_array($values[0])))
+ $values = array($values);
+ if($count > 1 && count($values[0]) !== $count)
+ {
+ throw new TDbException('dbtablegateway_pk_value_count_mismatch',
+ $this->getTableInfo()->getTableFullName());
+ }
+ return $this->getIndexKeyCondition($this->getTableInfo(),$primary, $values);
+ }
+
+ /**
+ * @param TDbConnection database connection.
+ * @param array values
+ * @return string quoted recursive tuple values, e.g. "('val1', 'val2')".
+ */
+ protected function quoteTuple($array)
+ {
+ $conn = $this->getDbConnection();
+ $data = array();
+ foreach($array as $k=>$v)
+ $data[] = is_array($v) ? $this->quoteTuple($v) : $conn->quoteString($v);
+ return '('.implode(', ', $data).')';
+ }
+
+ /**
+ * Create the condition and parameters for find by primary.
+ * @param array primary key values
+ * @return array tuple($where, $parameters)
+ */
+ protected function getPrimaryKeyCondition($values)
+ {
+ $primary = $this->getTableInfo()->getPrimaryKeys();
+ if(count($primary)===0)
+ {
+ throw new TDbException('dbtablegateway_no_primary_key_found',
+ $this->getTableInfo()->getTableFullName());
+ }
+ $criteria=array();
+ $bindings=array();
+ $i = 0;
+ foreach($primary as $key)
+ {
+ $column = $this->getTableInfo()->getColumn($key)->getColumnName();
+ $criteria[] = $column.' = :'.$key;
+ $bindings[$key] = isset($values[$key])?$values[$key]:$values[$i++];
+ }
+ return array(implode(' AND ', $criteria), $bindings);
+ }
+
+ /**
+ * Find one matching records for arbituary SQL.
+ * @param TSqlCriteria $criteria
+ * @return TDbDataReader record reader.
+ */
+ public function findBySql($criteria)
+ {
+ $command = $this->getSqlCommand($criteria);
+ return $this->onExecuteCommand($command, $command->queryRow());
+ }
+
+ /**
+ * Find zero or more matching records for arbituary SQL.
+ * @param TSqlCriteria $criteria
+ * @return TDbDataReader record reader.
+ */
+ public function findAllBySql($criteria)
+ {
+ $command = $this->getSqlCommand($criteria);
+ return $this->onExecuteCommand($command, $command->query());
+ }
+
+ /**
+ * Build sql command from the criteria. Limit, Offset and Ordering are applied if applicable.
+ * @param TSqlCriteria $criteria
+ * @return TDbCommand command corresponding to the criteria.
+ */
+ protected function getSqlCommand($criteria)
+ {
+ $sql = $criteria->getCondition();
+ $ordering = $criteria->getOrdersBy();
+ $limit = $criteria->getLimit();
+ $offset = $criteria->getOffset();
+ if(count($ordering) > 0)
+ $sql = $this->getBuilder()->applyOrdering($sql, $ordering);
+ if($limit>=0 || $offset>=0)
+ $sql = $this->getBuilder()->applyLimitOffset($sql, $limit, $offset);
+ $command = $this->getBuilder()->createCommand($sql);
+ $this->getBuilder()->bindArrayValues($command, $criteria->getParameters()->toArray());
+ $this->onCreateCommand($command, $criteria);
+ return $command;
+ }
+
+ /**
+ * @param TSqlCriteria $criteria
+ * @return integer number of records.
+ */
+ public function count($criteria)
+ {
+ if($criteria===null)
+ return (int)$this->getBuilder()->createCountCommand()->queryScalar();
+ $where = $criteria->getCondition();
+ $parameters = $criteria->getParameters()->toArray();
+ $ordering = $criteria->getOrdersBy();
+ $limit = $criteria->getLimit();
+ $offset = $criteria->getOffset();
+ $command = $this->getBuilder()->createCountCommand($where,$parameters,$ordering,$limit,$offset);
+ $this->onCreateCommand($command, $criteria);
+ return $this->onExecuteCommand($command, (int)$command->queryScalar());
+ }
+
+ /**
+ * Inserts a new record into the table. Each array key must
+ * correspond to a column name in the table unless a null value is permitted.
+ * @param array new record data.
+ * @return mixed last insert id if one column contains a serial or sequence,
+ * otherwise true if command executes successfully and affected 1 or more rows.
+ */
+ public function insert($data)
+ {
+ $command=$this->getBuilder()->createInsertCommand($data);
+ $this->onCreateCommand($command, new TSqlCriteria(null,$data));
+ $command->prepare();
+ if($this->onExecuteCommand($command, $command->execute()) > 0)
+ {
+ $value = $this->getLastInsertId();
+ return $value !== null ? $value : true;
+ }
+ return false;
+ }
+
+ /**
+ * Iterate through all the columns and returns the last insert id of the
+ * first column that has a sequence or serial.
+ * @return mixed last insert id, null if none is found.
+ */
+ public function getLastInsertID()
+ {
+ return $this->getBuilder()->getLastInsertID();
+ }
+
+ /**
+ * @param string __call method name
+ * @param string criteria conditions
+ * @param array method arguments
+ * @return TActiveRecordCriteria criteria created from the method name and its arguments.
+ */
+ public function createCriteriaFromString($method, $condition, $args)
+ {
+ $fields = $this->extractMatchingConditions($method, $condition);
+ $args=count($args) === 1 && is_array($args[0]) ? $args[0] : $args;
+ if(count($fields)>count($args))
+ {
+ throw new TDbException('dbtablegateway_mismatch_args_exception',
+ $method,count($fields),count($args));
+ }
+ return new TSqlCriteria(implode(' ',$fields), $args);
+ }
+
+ /**
+ * Calculates the AND/OR condition from dynamic method substrings using
+ * table meta data, allows for any AND-OR combinations.
+ * @param string dynamic method name
+ * @param string dynamic method search criteria
+ * @return array search condition substrings
+ */
+ protected function extractMatchingConditions($method, $condition)
+ {
+ $table = $this->getTableInfo();
+ $columns = $table->getLowerCaseColumnNames();
+ $regexp = '/('.implode('|', array_keys($columns)).')(and|_and_|or|_or_)?/i';
+ $matches = array();
+ if(!preg_match_all($regexp, strtolower($condition), $matches,PREG_SET_ORDER))
+ {
+ throw new TDbException('dbtablegateway_mismatch_column_name',
+ $method, implode(', ', $columns), $table->getTableFullName());
+ }
+
+ $fields = array();
+ foreach($matches as $match)
+ {
+ $key = $columns[$match[1]];
+ $column = $table->getColumn($key)->getColumnName();
+ $sql = $column . ' = ? ';
+ if(count($match) > 2)
+ $sql .= strtoupper(str_replace('_', '', $match[2]));
+ $fields[] = $sql;
+ }
+ return $fields;
+ }
+
+ /**
+ * Raised when a command is prepared and parameter binding is completed.
+ * The parameter object is TDataGatewayEventParameter of which the
+ * {@link TDataGatewayEventParameter::getCommand Command} property can be
+ * inspected to obtain the sql query to be executed.
+ * @param TDataGatewayCommand originator $sender
+ * @param TDataGatewayEventParameter
+ */
+ public function onCreateCommand($command, $criteria)
+ {
+ $this->raiseEvent('OnCreateCommand', $this, new TDataGatewayEventParameter($command,$criteria));
+ }
+
+ /**
+ * Raised when a command is executed and the result from the database was returned.
+ * The parameter object is TDataGatewayResultEventParameter of which the
+ * {@link TDataGatewayEventParameter::getResult Result} property contains
+ * the data return from the database. The data returned can be changed
+ * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
+ * @param TDataGatewayCommand originator $sender
+ * @param TDataGatewayResultEventParameter
+ */
+ public function onExecuteCommand($command, $result)
+ {
+ $parameter = new TDataGatewayResultEventParameter($command, $result);
+ $this->raiseEvent('OnExecuteCommand', $this, $parameter);
+ return $parameter->getResult();
+ }
+}
+
+/**
+ * TDataGatewayEventParameter class contains the TDbCommand to be executed as
+ * well as the criteria object.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.DataGateway
+ * @since 3.1
+ */
+class TDataGatewayEventParameter extends TEventParameter
+{
+ private $_command;
+ private $_criteria;
+
+ public function __construct($command,$criteria)
+ {
+ $this->_command=$command;
+ $this->_criteria=$criteria;
+ }
+
+ /**
+ * The database command to be executed. Do not rebind the parameters or change
+ * the sql query string.
+ * @return TDbCommand command to be executed.
+ */
+ public function getCommand()
+ {
+ return $this->_command;
+ }
+
+ /**
+ * @return TSqlCriteria criteria used to bind the sql query parameters.
+ */
+ public function getCriteria()
+ {
+ return $this->_criteria;
+ }
+}
+
+/**
+ * TDataGatewayResultEventParameter contains the TDbCommand executed and the resulting
+ * data returned from the database. The data can be changed by changing the
+ * {@link setResult Result} property.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.DataGateway
+ * @since 3.1
+ */
+class TDataGatewayResultEventParameter extends TEventParameter
+{
+ private $_command;
+ private $_result;
+
+ public function __construct($command,$result)
+ {
+ $this->_command=$command;
+ $this->_result=$result;
+ }
+
+ /**
+ * @return TDbCommand database command executed.
+ */
+ public function getCommand()
+ {
+ return $this->_command;
+ }
+
+ /**
+ * @return mixed result returned from executing the command.
+ */
+ public function getResult()
+ {
+ return $this->_result;
+ }
+
+ /**
+ * @param mixed change the result returned by the gateway.
+ */
+ public function setResult($value)
+ {
+ $this->_result=$value;
+ }
+}
+
+?>
diff --git a/framework/Data/DataGateway/TSqlCriteria.php b/framework/Data/DataGateway/TSqlCriteria.php
index a7da3adf..3a54f4c4 100644
--- a/framework/Data/DataGateway/TSqlCriteria.php
+++ b/framework/Data/DataGateway/TSqlCriteria.php
@@ -1,284 +1,284 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
- * @package System.Data.DataGateway
- */
-
-/**
- * Search criteria for TDbDataGateway.
- *
- * Criteria object for data gateway finder methods. Usage:
- *
- * $criteria = new TSqlCriteria();
- * $criteria->Parameters[':name'] = 'admin';
- * $criteria->Parameters[':pass'] = 'prado';
- * $criteria->OrdersBy['level'] = 'desc';
- * $criteria->OrdersBy['name'] = 'asc';
- * $criteria->Limit = 10;
- * $criteria->Offset = 20;
- *
- *
- * @author Wei Zhuo
- * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TSqlCriteria extends TComponent
-{
- /**
- * @var mixed
- * @since 3.1.7
- */
- private $_select='*';
- private $_condition;
- private $_parameters;
- private $_ordersBy;
- private $_limit;
- private $_offset;
-
- /**
- * Creates a new criteria with given condition;
- * @param string sql string after the WHERE stanza
- * @param mixed named or indexed parameters, accepts as multiple arguments.
- */
- public function __construct($condition=null, $parameters=array())
- {
- if(!is_array($parameters) && func_num_args() > 1)
- $parameters = array_slice(func_get_args(),1);
- $this->_parameters=new TAttributeCollection;
- $this->_parameters->setCaseSensitive(true);
- $this->_parameters->copyFrom((array)$parameters);
- $this->_ordersBy=new TAttributeCollection;
- $this->_ordersBy->setCaseSensitive(true);
-
- $this->setCondition($condition);
- }
-
- /**
- * Gets the field list to be placed after the SELECT in the SQL. Default to '*'
- * @return mixed
- * @since 3.1.7
- */
- public function getSelect()
- {
- return $this->_select;
- }
-
- /**
- * Sets the field list to be placed after the SELECT in the SQL.
- *
- * Different behavior depends on type of assigned value
- * string
- * usage without modification
- *
- * null
- * will be expanded to full list of quoted table column names (quoting depends on database)
- *
- * array
- * - Column names will be quoted if used as key or value of array
- *
- * array('col1', 'col2', 'col2')
- * // SELECT `col1`, `col2`, `col3` FROM...
- *
- *
- * - Column aliasing
- *
- * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
- * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
- *
- *
- * - NULL and scalar values (strings will be quoted depending on database)
- *
- * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
- * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
- *
- *
- * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
- *
- * array('col1' => 'NULL', '*')
- * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
- *
- *
- * @param mixed
- * @since 3.1.7
- * @see TDbCommandBuilder::getSelectFieldList()
- */
- public function setSelect($value)
- {
- $this->_select = $value;
- }
-
- /**
- * @return string search conditions.
- */
- public function getCondition()
- {
- return $this->_condition;
- }
-
- /**
- * Sets the search conditions to be placed after the WHERE clause in the SQL.
- * @param string search conditions.
- */
- public function setCondition($value)
- {
- if(empty($value)) {
- return;
- }
-
- // supporting the following SELECT-syntax:
- // [ORDER BY {col_name | expr | position}
- // [ASC | DESC], ...]
- // [LIMIT {[offset,] row_count | row_count OFFSET offset}]
- // See: http://dev.mysql.com/doc/refman/5.0/en/select.html
-
- if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) {
- // condition contains ORDER BY
- $value = str_replace($matches[0], '', $value);
- if(strlen($matches[1]) > 0) {
- $this->setOrdersBy($matches[1]);
- } else if(strlen($matches[2]) > 0) {
- $this->setOrdersBy($matches[2]);
- }
- }
-
- if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) {
- // condition contains limit
- $value = str_replace($matches[0], '', $value); // remove limit from query
- if(strpos($matches[1], ',')) { // both offset and limit given
- list($offset, $limit) = explode(',', $matches[1]);
- $this->_limit = (int)$limit;
- $this->_offset = (int)$offset;
- } else { // only limit given
- $this->_limit = (int)$matches[1];
- }
- }
-
- if(preg_match('/OFFSET\s+(\d+)/i', $value, $matches) > 0) {
- // condition contains offset
- $value = str_replace($matches[0], '', $value); // remove offset from query
- $this->_offset = (int)$matches[1]; // set offset in criteria
- }
-
- $this->_condition = trim($value);
- }
-
- /**
- * @return TAttributeCollection list of named parameters and values.
- */
- public function getParameters()
- {
- return $this->_parameters;
- }
-
- /**
- * @param ArrayAccess named parameters.
- */
- public function setParameters($value)
- {
- if(!(is_array($value) || $value instanceof ArrayAccess))
- throw new TException('value must be array or ArrayAccess');
- $this->_parameters->copyFrom($value);
- }
-
- /**
- * @return boolean true if the parameter index are string base, false otherwise.
- */
- public function getIsNamedParameters()
- {
- foreach($this->getParameters() as $k=>$v)
- return is_string($k);
- }
-
- /**
- * @return TAttributeCollection ordering clause.
- */
- public function getOrdersBy()
- {
- return $this->_ordersBy;
- }
-
- /**
- * @param mixed ordering clause.
- */
- public function setOrdersBy($value)
- {
- if(is_array($value) || $value instanceof Traversable)
- $this->_ordersBy->copyFrom($value);
- else
- {
- $value=trim(preg_replace('/\s+/',' ',(string)$value));
- $orderBys=array();
- foreach(explode(',',$value) as $orderBy)
- {
- $vs=explode(' ',trim($orderBy));
- $orderBys[$vs[0]]=isset($vs[1])?$vs[1]:'asc';
- }
- $this->_ordersBy->copyFrom($orderBys);
- }
- }
-
- /**
- * @return int maximum number of records to return.
- */
- public function getLimit()
- {
- return $this->_limit;
- }
-
- /**
- * @param int maximum number of records to return.
- */
- public function setLimit($value)
- {
- $this->_limit=$value;
- }
-
- /**
- * @return int record offset.
- */
- public function getOffset()
- {
- return $this->_offset;
- }
-
- /**
- * @param int record offset.
- */
- public function setOffset($value)
- {
- $this->_offset=$value;
- }
-
- /**
- * @return string string representation of the parameters. Useful for debugging.
- */
- public function __toString()
- {
- $str = '';
- if(strlen((string)$this->getCondition()) > 0)
- $str .= '"'.(string)$this->getCondition().'"';
- $params = array();
- foreach($this->getParameters() as $k=>$v)
- $params[] = "{$k} => ${v}";
- if(count($params) > 0)
- $str .= ', "'.implode(', ',$params).'"';
- $orders = array();
- foreach($this->getOrdersBy() as $k=>$v)
- $orders[] = "{$k} => ${v}";
- if(count($orders) > 0)
- $str .= ', "'.implode(', ',$orders).'"';
- if($this->_limit !==null)
- $str .= ', '.$this->_limit;
- if($this->_offset !== null)
- $str .= ', '.$this->_offset;
- return $str;
- }
-}
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
+ * @package System.Data.DataGateway
+ */
+
+/**
+ * Search criteria for TDbDataGateway.
+ *
+ * Criteria object for data gateway finder methods. Usage:
+ *
+ * $criteria = new TSqlCriteria();
+ * $criteria->Parameters[':name'] = 'admin';
+ * $criteria->Parameters[':pass'] = 'prado';
+ * $criteria->OrdersBy['level'] = 'desc';
+ * $criteria->OrdersBy['name'] = 'asc';
+ * $criteria->Limit = 10;
+ * $criteria->Offset = 20;
+ *
+ *
+ * @author Wei Zhuo
+ * @version $Id: TDbSqlCriteria.php 1835 2007-04-03 01:38:15Z wei $
+ * @package System.Data.DataGateway
+ * @since 3.1
+ */
+class TSqlCriteria extends TComponent
+{
+ /**
+ * @var mixed
+ * @since 3.1.7
+ */
+ private $_select='*';
+ private $_condition;
+ private $_parameters;
+ private $_ordersBy;
+ private $_limit;
+ private $_offset;
+
+ /**
+ * Creates a new criteria with given condition;
+ * @param string sql string after the WHERE stanza
+ * @param mixed named or indexed parameters, accepts as multiple arguments.
+ */
+ public function __construct($condition=null, $parameters=array())
+ {
+ if(!is_array($parameters) && func_num_args() > 1)
+ $parameters = array_slice(func_get_args(),1);
+ $this->_parameters=new TAttributeCollection;
+ $this->_parameters->setCaseSensitive(true);
+ $this->_parameters->copyFrom((array)$parameters);
+ $this->_ordersBy=new TAttributeCollection;
+ $this->_ordersBy->setCaseSensitive(true);
+
+ $this->setCondition($condition);
+ }
+
+ /**
+ * Gets the field list to be placed after the SELECT in the SQL. Default to '*'
+ * @return mixed
+ * @since 3.1.7
+ */
+ public function getSelect()
+ {
+ return $this->_select;
+ }
+
+ /**
+ * Sets the field list to be placed after the SELECT in the SQL.
+ *
+ * Different behavior depends on type of assigned value
+ * string
+ * usage without modification
+ *
+ * null
+ * will be expanded to full list of quoted table column names (quoting depends on database)
+ *
+ * array
+ * - Column names will be quoted if used as key or value of array
+ *
+ * array('col1', 'col2', 'col2')
+ * // SELECT `col1`, `col2`, `col3` FROM...
+ *
+ *
+ * - Column aliasing
+ *
+ * array('mycol1' => 'col1', 'mycol2' => 'COUNT(*)')
+ * // SELECT `col1` AS mycol1, COUNT(*) AS mycol2 FROM...
+ *
+ *
+ * - NULL and scalar values (strings will be quoted depending on database)
+ *
+ * array('col1' => 'my custom string', 'col2' => 1.0, 'col3' => 'NULL')
+ * // SELECT "my custom string" AS `col1`, 1.0 AS `col2`, NULL AS `col3` FROM...
+ *
+ *
+ * - If the *-wildcard char is used as key or value, add the full list of quoted table column names
+ *
+ * array('col1' => 'NULL', '*')
+ * // SELECT `col1`, `col2`, `col3`, NULL AS `col1` FROM...
+ *
+ *
+ * @param mixed
+ * @since 3.1.7
+ * @see TDbCommandBuilder::getSelectFieldList()
+ */
+ public function setSelect($value)
+ {
+ $this->_select = $value;
+ }
+
+ /**
+ * @return string search conditions.
+ */
+ public function getCondition()
+ {
+ return $this->_condition;
+ }
+
+ /**
+ * Sets the search conditions to be placed after the WHERE clause in the SQL.
+ * @param string search conditions.
+ */
+ public function setCondition($value)
+ {
+ if(empty($value)) {
+ return;
+ }
+
+ // supporting the following SELECT-syntax:
+ // [ORDER BY {col_name | expr | position}
+ // [ASC | DESC], ...]
+ // [LIMIT {[offset,] row_count | row_count OFFSET offset}]
+ // See: http://dev.mysql.com/doc/refman/5.0/en/select.html
+
+ if(preg_match('/ORDER\s+BY\s+(.*?)(?=LIMIT)|ORDER\s+BY\s+(.*?)$/i', $value, $matches) > 0) {
+ // condition contains ORDER BY
+ $value = str_replace($matches[0], '', $value);
+ if(strlen($matches[1]) > 0) {
+ $this->setOrdersBy($matches[1]);
+ } else if(strlen($matches[2]) > 0) {
+ $this->setOrdersBy($matches[2]);
+ }
+ }
+
+ if(preg_match('/LIMIT\s+([\d\s,]+)/i', $value, $matches) > 0) {
+ // condition contains limit
+ $value = str_replace($matches[0], '', $value); // remove limit from query
+ if(strpos($matches[1], ',')) { // both offset and limit given
+ list($offset, $limit) = explode(',', $matches[1]);
+ $this->_limit = (int)$limit;
+ $this->_offset = (int)$offset;
+ } else { // only limit given
+ $this->_limit = (int)$matches[1];
+ }
+ }
+
+ if(preg_match('/OFFSET\s+(\d+)/i', $value, $matches) > 0) {
+ // condition contains offset
+ $value = str_replace($matches[0], '', $value); // remove offset from query
+ $this->_offset = (int)$matches[1]; // set offset in criteria
+ }
+
+ $this->_condition = trim($value);
+ }
+
+ /**
+ * @return TAttributeCollection list of named parameters and values.
+ */
+ public function getParameters()
+ {
+ return $this->_parameters;
+ }
+
+ /**
+ * @param ArrayAccess named parameters.
+ */
+ public function setParameters($value)
+ {
+ if(!(is_array($value) || $value instanceof ArrayAccess))
+ throw new TException('value must be array or ArrayAccess');
+ $this->_parameters->copyFrom($value);
+ }
+
+ /**
+ * @return boolean true if the parameter index are string base, false otherwise.
+ */
+ public function getIsNamedParameters()
+ {
+ foreach($this->getParameters() as $k=>$v)
+ return is_string($k);
+ }
+
+ /**
+ * @return TAttributeCollection ordering clause.
+ */
+ public function getOrdersBy()
+ {
+ return $this->_ordersBy;
+ }
+
+ /**
+ * @param mixed ordering clause.
+ */
+ public function setOrdersBy($value)
+ {
+ if(is_array($value) || $value instanceof Traversable)
+ $this->_ordersBy->copyFrom($value);
+ else
+ {
+ $value=trim(preg_replace('/\s+/',' ',(string)$value));
+ $orderBys=array();
+ foreach(explode(',',$value) as $orderBy)
+ {
+ $vs=explode(' ',trim($orderBy));
+ $orderBys[$vs[0]]=isset($vs[1])?$vs[1]:'asc';
+ }
+ $this->_ordersBy->copyFrom($orderBys);
+ }
+ }
+
+ /**
+ * @return int maximum number of records to return.
+ */
+ public function getLimit()
+ {
+ return $this->_limit;
+ }
+
+ /**
+ * @param int maximum number of records to return.
+ */
+ public function setLimit($value)
+ {
+ $this->_limit=$value;
+ }
+
+ /**
+ * @return int record offset.
+ */
+ public function getOffset()
+ {
+ return $this->_offset;
+ }
+
+ /**
+ * @param int record offset.
+ */
+ public function setOffset($value)
+ {
+ $this->_offset=$value;
+ }
+
+ /**
+ * @return string string representation of the parameters. Useful for debugging.
+ */
+ public function __toString()
+ {
+ $str = '';
+ if(strlen((string)$this->getCondition()) > 0)
+ $str .= '"'.(string)$this->getCondition().'"';
+ $params = array();
+ foreach($this->getParameters() as $k=>$v)
+ $params[] = "{$k} => ${v}";
+ if(count($params) > 0)
+ $str .= ', "'.implode(', ',$params).'"';
+ $orders = array();
+ foreach($this->getOrdersBy() as $k=>$v)
+ $orders[] = "{$k} => ${v}";
+ if(count($orders) > 0)
+ $str .= ', "'.implode(', ',$orders).'"';
+ if($this->_limit !==null)
+ $str .= ', '.$this->_limit;
+ if($this->_offset !== null)
+ $str .= ', '.$this->_offset;
+ return $str;
+ }
+}
?>
\ No newline at end of file
diff --git a/framework/Data/DataGateway/TTableGateway.php b/framework/Data/DataGateway/TTableGateway.php
index bdcb391e..c436e0b0 100644
--- a/framework/Data/DataGateway/TTableGateway.php
+++ b/framework/Data/DataGateway/TTableGateway.php
@@ -1,476 +1,476 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.DataGateway
- */
-
-/**
- * Loads the data gateway command builder and sql criteria.
- */
-Prado::using('System.Data.DataGateway.TSqlCriteria');
-Prado::using('System.Data.DataGateway.TDataGatewayCommand');
-
-/**
- * TTableGateway class provides several find methods to get data from the database
- * and update, insert, and delete methods.
- *
- * Each method maps the input parameters into a SQL call and executes the SQL
- * against a database connection. The TTableGateway is stateless
- * (with respect to the data and data objects), as its role is to push data back and forth.
- *
- * Example usage:
- *
- * //create a connection
- * $dsn = 'pgsql:host=localhost;dbname=test';
- * $conn = new TDbConnection($dsn, 'dbuser','dbpass');
- *
- * //create a table gateway for table/view named 'address'
- * $table = new TTableGateway('address', $conn);
- *
- * //insert a new row, returns last insert id (if applicable)
- * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111'));
- *
- * $record1 = $table->findByPk($id); //find inserted record
- *
- * //finds all records, returns an iterator
- * $records = $table->findAll();
- * print_r($records->readAll());
- *
- * //update the row
- * $table->updateByPk($record1, $id);
- *
- *
- * All methods that may return more than one row of data will return an
- * TDbDataReader iterator.
- *
- * The OnCreateCommand event is raised when a command is prepared and parameter
- * binding is completed. The parameter object is a TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- *
- * The OnExecuteCommand event is raised when a command is executed and the result
- * from the database was returned. The parameter object is a
- * TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- *
- *
- * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement
- * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj
- *
- * function log_it($sender, $param)
- * {
- * var_dump($param); //TDataGatewayEventParameter object.
- * }
- *
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.DataGateway
- * @since 3.1
- */
-class TTableGateway extends TComponent
-{
- private $_command;
- private $_connection;
-
- /**
- * Creates a new generic table gateway for a given table or view name
- * and a database connection.
- * @param string|TDbTableInfo table or view name or table information.
- * @param TDbConnection database connection.
- */
- public function __construct($table,$connection)
- {
- $this->_connection=$connection;
- if(is_string($table))
- $this->setTableName($table);
- else if($table instanceof TDbTableInfo)
- $this->setTableInfo($table);
- else
- throw new TDbException('dbtablegateway_invalid_table_info');
- }
-
- /**
- * @param TDbTableInfo table or view information.
- */
- protected function setTableInfo($tableInfo)
- {
- $builder = $tableInfo->createCommandBuilder($this->getDbConnection());
- $this->initCommandBuilder($builder);
- }
-
- /**
- * Sets up the command builder for the given table.
- * @param string table or view name.
- */
- protected function setTableName($tableName)
- {
- Prado::using('System.Data.Common.TDbMetaData');
- $meta = TDbMetaData::getInstance($this->getDbConnection());
- $this->initCommandBuilder($meta->createCommandBuilder($tableName));
- }
-
- public function getTableInfo()
- {
- return $this->getCommand()->getTableInfo();
- }
-
- public function getTableName()
- {
- return $this->getTableInfo()->getTableName();
- }
-
- /**
- * @param TDbCommandBuilder database specific command builder.
- */
- protected function initCommandBuilder($builder)
- {
- $this->_command = new TDataGatewayCommand($builder);
- $this->_command->OnCreateCommand[] = array($this, 'onCreateCommand');
- $this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand');
- }
-
- /**
- * Raised when a command is prepared and parameter binding is completed.
- * The parameter object is TDataGatewayEventParameter of which the
- * {@link TDataGatewayEventParameter::getCommand Command} property can be
- * inspected to obtain the sql query to be executed.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayEventParameter
- */
- public function onCreateCommand($sender, $param)
- {
- $this->raiseEvent('OnCreateCommand', $this, $param);
- }
-
- /**
- * Raised when a command is executed and the result from the database was returned.
- * The parameter object is TDataGatewayResultEventParameter of which the
- * {@link TDataGatewayEventParameter::getResult Result} property contains
- * the data return from the database. The data returned can be changed
- * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
- * @param TDataGatewayCommand originator $sender
- * @param TDataGatewayResultEventParameter
- */
- public function onExecuteCommand($sender, $param)
- {
- $this->raiseEvent('OnExecuteCommand', $this, $param);
- }
-
- /**
- * @return TDataGatewayCommand command builder and executor.
- */
- protected function getCommand()
- {
- return $this->_command;
- }
-
- /**
- * @return TDbConnection database connection.
- */
- public function getDbConnection()
- {
- return $this->_connection;
- }
-
- /**
- * Execute arbituary sql command with binding parameters.
- * @param string SQL query string.
- * @param array binding parameters, positional or named.
- * @return array query results.
- */
- public function findBySql($sql, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($sql,$parameters, $args);
- return $this->getCommand()->findBySql($criteria);
- }
-
- /**
- * Execute arbituary sql command with binding parameters.
- * @param string SQL query string.
- * @param array binding parameters, positional or named.
- * @return TDbDataReader query results.
- */
- public function findAllBySql($sql, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($sql,$parameters, $args);
- return $this->getCommand()->findAllBySql($criteria);
- }
-
- /**
- * Find one single record that matches the criteria.
- *
- * Usage:
- *
- * $table->find('username = :name AND password = :pass',
- * array(':name'=>$name, ':pass'=>$pass));
- * $table->find('username = ? AND password = ?', array($name, $pass));
- * $table->find('username = ? AND password = ?', $name, $pass);
- * //$criteria is of TSqlCriteria
- * $table->find($criteria); //the 2nd parameter for find() is ignored.
- *
- *
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return array matching record object.
- */
- public function find($criteria, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->find($criteria);
- }
-
- /**
- * Accepts same parameters as find(), but returns TDbDataReader instead.
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return TDbDataReader matching records.
- */
- public function findAll($criteria=null, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- if($criteria!==null)
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->findAll($criteria);
- }
-
- /**
- * Find one record using only the primary key or composite primary keys. Usage:
- *
- *
- * $table->findByPk($primaryKey);
- * $table->findByPk($key1, $key2, ...);
- * $table->findByPk(array($key1,$key2,...));
- *
- *
- * @param mixed primary keys
- * @return array matching record.
- */
- public function findByPk($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->findByPk($keys);
- }
-
- /**
- * Similar to findByPk(), but returns TDbDataReader instead.
- *
- * For scalar primary keys:
- *
- * $table->findAllByPk($key1, $key2, ...);
- * $table->findAllByPk(array($key1, $key2, ...));
- *
- *
- * For composite keys:
- *
- * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...);
- * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...));
- *
- * @param mixed primary keys
- * @return TDbDataReader data reader.
- */
- public function findAllByPks($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->findAllByPk($keys);
- }
-
- /**
- * Delete records from the table with condition given by $where and
- * binding values specified by $parameter argument.
- * This method uses additional arguments as $parameters. E.g.
- *
- * $table->delete('age > ? AND location = ?', $age, $location);
- *
- * @param string delete condition.
- * @param array condition parameters.
- * @return integer number of records deleted.
- */
- public function deleteAll($criteria, $parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->delete($criteria);
- }
-
- /**
- * Delete records by primary key. Usage:
- *
- *
- * $table->deleteByPk($primaryKey); //delete 1 record
- * $table->deleteByPk($key1,$key2,...); //delete multiple records
- * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records
- *
- *
- * For composite primary keys (determined from the table definitions):
- *
- * $table->deleteByPk(array($key1,$key2)); //delete 1 record
- *
- * //delete multiple records
- * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...);
- *
- * //delete multiple records
- * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. ));
- *
- *
- * @param mixed primary key values.
- * @return int number of records deleted.
- */
- public function deleteByPk($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->getCommand()->deleteByPk($keys);
- }
-
- /**
- * Alias for deleteByPk()
- */
- public function deleteAllByPks($keys)
- {
- if(func_num_args() > 1)
- $keys = func_get_args();
- return $this->deleteByPk($keys);
- }
-
- /**
- * Find the number of records.
- * @param string|TSqlCriteria SQL condition or criteria object.
- * @param mixed parameter values.
- * @return int number of records.
- */
- public function count($criteria=null,$parameters=array())
- {
- $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
- if($criteria!==null)
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->count($criteria);
- }
-
- /**
- * Updates the table with new name-value pair $data. Each array key must
- * correspond to a column name in the table. The update condition is
- * specified by the $where argument and additional binding values can be
- * specified using the $parameter argument.
- * This method uses additional arguments as $parameters. E.g.
- *
- * $gateway->update($data, 'age > ? AND location = ?', $age, $location);
- *
- * @param array new record data.
- * @param string update condition
- * @param array additional binding name-value pairs.
- * @return integer number of records updated.
- */
- public function update($data, $criteria, $parameters=array())
- {
- $args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null;
- $criteria = $this->getCriteria($criteria,$parameters, $args);
- return $this->getCommand()->update($data, $criteria);
- }
-
- /**
- * Inserts a new record into the table. Each array key must
- * correspond to a column name in the table unless a null value is permitted.
- * @param array new record data.
- * @return mixed last insert id if one column contains a serial or sequence,
- * otherwise true if command executes successfully and affected 1 or more rows.
- */
- public function insert($data)
- {
- return $this->getCommand()->insert($data);
- }
-
- /**
- * @return mixed last insert id, null if none is found.
- */
- public function getLastInsertId()
- {
- return $this->getCommand()->getLastInsertId();
- }
-
- /**
- * Create a new TSqlCriteria object from a string $criteria. The $args
- * are additional parameters and are used in place of the $parameters
- * if $parameters is not an array and $args is an arrary.
- * @param string|TSqlCriteria sql criteria
- * @param mixed parameters passed by the user.
- * @param array additional parameters obtained from function_get_args().
- * @return TSqlCriteria criteria object.
- */
- protected function getCriteria($criteria, $parameters, $args)
- {
- if(is_string($criteria))
- {
- $useArgs = !is_array($parameters) && is_array($args);
- return new TSqlCriteria($criteria,$useArgs ? $args : $parameters);
- }
- else if($criteria instanceof TSqlCriteria)
- return $criteria;
- else
- throw new TDbException('dbtablegateway_invalid_criteria');
- }
-
- /**
- * Dynamic find method using parts of method name as search criteria.
- * Method name starting with "findBy" only returns 1 record.
- * Method name starting with "findAllBy" returns 0 or more records.
- * Method name starting with "deleteBy" deletes records by the trail criteria.
- * The condition is taken as part of the method name after "findBy", "findAllBy"
- * or "deleteBy".
- *
- * The following are equivalent:
- *
- * $table->findByName($name)
- * $table->find('Name = ?', $name);
- *
- *
- * $table->findByUsernameAndPassword($name,$pass); // OR may be used
- * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used
- * $table->find('Username = ? AND Password = ?', $name, $pass);
- *
- *
- * $table->findAllByAge($age);
- * $table->findAll('Age = ?', $age);
- *
- *
- * $table->deleteAll('Name = ?', $name);
- * $table->deleteByName($name);
- *
- * @return mixed single record if method name starts with "findBy", 0 or more records
- * if method name starts with "findAllBy"
- */
- public function __call($method,$args)
- {
- $delete =false;
- if($findOne = substr(strtolower($method),0,6)==='findby')
- $condition = $method[6]==='_' ? substr($method,7) : substr($method,6);
- else if(substr(strtolower($method),0,9)==='findallby')
- $condition = $method[9]==='_' ? substr($method,10) : substr($method,9);
- else if($delete = substr(strtolower($method),0,8)==='deleteby')
- $condition = $method[8]==='_' ? substr($method,9) : substr($method,8);
- else if($delete = substr(strtolower($method),0,11)==='deleteallby')
- $condition = $method[11]==='_' ? substr($method,12) : substr($method,11);
- else
- return null;
-
- $criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args);
- if($delete)
- return $this->deleteAll($criteria);
- else
- return $findOne ? $this->find($criteria) : $this->findAll($criteria);
- }
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.DataGateway
+ */
+
+/**
+ * Loads the data gateway command builder and sql criteria.
+ */
+Prado::using('System.Data.DataGateway.TSqlCriteria');
+Prado::using('System.Data.DataGateway.TDataGatewayCommand');
+
+/**
+ * TTableGateway class provides several find methods to get data from the database
+ * and update, insert, and delete methods.
+ *
+ * Each method maps the input parameters into a SQL call and executes the SQL
+ * against a database connection. The TTableGateway is stateless
+ * (with respect to the data and data objects), as its role is to push data back and forth.
+ *
+ * Example usage:
+ *
+ * //create a connection
+ * $dsn = 'pgsql:host=localhost;dbname=test';
+ * $conn = new TDbConnection($dsn, 'dbuser','dbpass');
+ *
+ * //create a table gateway for table/view named 'address'
+ * $table = new TTableGateway('address', $conn);
+ *
+ * //insert a new row, returns last insert id (if applicable)
+ * $id = $table->insert(array('name'=>'wei', 'phone'=>'111111'));
+ *
+ * $record1 = $table->findByPk($id); //find inserted record
+ *
+ * //finds all records, returns an iterator
+ * $records = $table->findAll();
+ * print_r($records->readAll());
+ *
+ * //update the row
+ * $table->updateByPk($record1, $id);
+ *
+ *
+ * All methods that may return more than one row of data will return an
+ * TDbDataReader iterator.
+ *
+ * The OnCreateCommand event is raised when a command is prepared and parameter
+ * binding is completed. The parameter object is a TDataGatewayEventParameter of which the
+ * {@link TDataGatewayEventParameter::getCommand Command} property can be
+ * inspected to obtain the sql query to be executed.
+ *
+ * The OnExecuteCommand event is raised when a command is executed and the result
+ * from the database was returned. The parameter object is a
+ * TDataGatewayResultEventParameter of which the
+ * {@link TDataGatewayEventParameter::getResult Result} property contains
+ * the data return from the database. The data returned can be changed
+ * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
+ *
+ *
+ * $table->OnCreateCommand[] = 'log_it'; //any valid PHP callback statement
+ * $table->OnExecuteCommand[] = array($obj, 'method_name'); // calls 'method_name' on $obj
+ *
+ * function log_it($sender, $param)
+ * {
+ * var_dump($param); //TDataGatewayEventParameter object.
+ * }
+ *
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.DataGateway
+ * @since 3.1
+ */
+class TTableGateway extends TComponent
+{
+ private $_command;
+ private $_connection;
+
+ /**
+ * Creates a new generic table gateway for a given table or view name
+ * and a database connection.
+ * @param string|TDbTableInfo table or view name or table information.
+ * @param TDbConnection database connection.
+ */
+ public function __construct($table,$connection)
+ {
+ $this->_connection=$connection;
+ if(is_string($table))
+ $this->setTableName($table);
+ else if($table instanceof TDbTableInfo)
+ $this->setTableInfo($table);
+ else
+ throw new TDbException('dbtablegateway_invalid_table_info');
+ }
+
+ /**
+ * @param TDbTableInfo table or view information.
+ */
+ protected function setTableInfo($tableInfo)
+ {
+ $builder = $tableInfo->createCommandBuilder($this->getDbConnection());
+ $this->initCommandBuilder($builder);
+ }
+
+ /**
+ * Sets up the command builder for the given table.
+ * @param string table or view name.
+ */
+ protected function setTableName($tableName)
+ {
+ Prado::using('System.Data.Common.TDbMetaData');
+ $meta = TDbMetaData::getInstance($this->getDbConnection());
+ $this->initCommandBuilder($meta->createCommandBuilder($tableName));
+ }
+
+ public function getTableInfo()
+ {
+ return $this->getCommand()->getTableInfo();
+ }
+
+ public function getTableName()
+ {
+ return $this->getTableInfo()->getTableName();
+ }
+
+ /**
+ * @param TDbCommandBuilder database specific command builder.
+ */
+ protected function initCommandBuilder($builder)
+ {
+ $this->_command = new TDataGatewayCommand($builder);
+ $this->_command->OnCreateCommand[] = array($this, 'onCreateCommand');
+ $this->_command->OnExecuteCommand[] = array($this, 'onExecuteCommand');
+ }
+
+ /**
+ * Raised when a command is prepared and parameter binding is completed.
+ * The parameter object is TDataGatewayEventParameter of which the
+ * {@link TDataGatewayEventParameter::getCommand Command} property can be
+ * inspected to obtain the sql query to be executed.
+ * @param TDataGatewayCommand originator $sender
+ * @param TDataGatewayEventParameter
+ */
+ public function onCreateCommand($sender, $param)
+ {
+ $this->raiseEvent('OnCreateCommand', $this, $param);
+ }
+
+ /**
+ * Raised when a command is executed and the result from the database was returned.
+ * The parameter object is TDataGatewayResultEventParameter of which the
+ * {@link TDataGatewayEventParameter::getResult Result} property contains
+ * the data return from the database. The data returned can be changed
+ * by setting the {@link TDataGatewayEventParameter::setResult Result} property.
+ * @param TDataGatewayCommand originator $sender
+ * @param TDataGatewayResultEventParameter
+ */
+ public function onExecuteCommand($sender, $param)
+ {
+ $this->raiseEvent('OnExecuteCommand', $this, $param);
+ }
+
+ /**
+ * @return TDataGatewayCommand command builder and executor.
+ */
+ protected function getCommand()
+ {
+ return $this->_command;
+ }
+
+ /**
+ * @return TDbConnection database connection.
+ */
+ public function getDbConnection()
+ {
+ return $this->_connection;
+ }
+
+ /**
+ * Execute arbituary sql command with binding parameters.
+ * @param string SQL query string.
+ * @param array binding parameters, positional or named.
+ * @return array query results.
+ */
+ public function findBySql($sql, $parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ $criteria = $this->getCriteria($sql,$parameters, $args);
+ return $this->getCommand()->findBySql($criteria);
+ }
+
+ /**
+ * Execute arbituary sql command with binding parameters.
+ * @param string SQL query string.
+ * @param array binding parameters, positional or named.
+ * @return TDbDataReader query results.
+ */
+ public function findAllBySql($sql, $parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ $criteria = $this->getCriteria($sql,$parameters, $args);
+ return $this->getCommand()->findAllBySql($criteria);
+ }
+
+ /**
+ * Find one single record that matches the criteria.
+ *
+ * Usage:
+ *
+ * $table->find('username = :name AND password = :pass',
+ * array(':name'=>$name, ':pass'=>$pass));
+ * $table->find('username = ? AND password = ?', array($name, $pass));
+ * $table->find('username = ? AND password = ?', $name, $pass);
+ * //$criteria is of TSqlCriteria
+ * $table->find($criteria); //the 2nd parameter for find() is ignored.
+ *
+ *
+ * @param string|TSqlCriteria SQL condition or criteria object.
+ * @param mixed parameter values.
+ * @return array matching record object.
+ */
+ public function find($criteria, $parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ $criteria = $this->getCriteria($criteria,$parameters, $args);
+ return $this->getCommand()->find($criteria);
+ }
+
+ /**
+ * Accepts same parameters as find(), but returns TDbDataReader instead.
+ * @param string|TSqlCriteria SQL condition or criteria object.
+ * @param mixed parameter values.
+ * @return TDbDataReader matching records.
+ */
+ public function findAll($criteria=null, $parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ if($criteria!==null)
+ $criteria = $this->getCriteria($criteria,$parameters, $args);
+ return $this->getCommand()->findAll($criteria);
+ }
+
+ /**
+ * Find one record using only the primary key or composite primary keys. Usage:
+ *
+ *
+ * $table->findByPk($primaryKey);
+ * $table->findByPk($key1, $key2, ...);
+ * $table->findByPk(array($key1,$key2,...));
+ *
+ *
+ * @param mixed primary keys
+ * @return array matching record.
+ */
+ public function findByPk($keys)
+ {
+ if(func_num_args() > 1)
+ $keys = func_get_args();
+ return $this->getCommand()->findByPk($keys);
+ }
+
+ /**
+ * Similar to findByPk(), but returns TDbDataReader instead.
+ *
+ * For scalar primary keys:
+ *
+ * $table->findAllByPk($key1, $key2, ...);
+ * $table->findAllByPk(array($key1, $key2, ...));
+ *
+ *
+ * For composite keys:
+ *
+ * $table->findAllByPk(array($key1, $key2), array($key3, $key4), ...);
+ * $table->findAllByPk(array(array($key1, $key2), array($key3, $key4), ...));
+ *
+ * @param mixed primary keys
+ * @return TDbDataReader data reader.
+ */
+ public function findAllByPks($keys)
+ {
+ if(func_num_args() > 1)
+ $keys = func_get_args();
+ return $this->getCommand()->findAllByPk($keys);
+ }
+
+ /**
+ * Delete records from the table with condition given by $where and
+ * binding values specified by $parameter argument.
+ * This method uses additional arguments as $parameters. E.g.
+ *
+ * $table->delete('age > ? AND location = ?', $age, $location);
+ *
+ * @param string delete condition.
+ * @param array condition parameters.
+ * @return integer number of records deleted.
+ */
+ public function deleteAll($criteria, $parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ $criteria = $this->getCriteria($criteria,$parameters, $args);
+ return $this->getCommand()->delete($criteria);
+ }
+
+ /**
+ * Delete records by primary key. Usage:
+ *
+ *
+ * $table->deleteByPk($primaryKey); //delete 1 record
+ * $table->deleteByPk($key1,$key2,...); //delete multiple records
+ * $table->deleteByPk(array($key1,$key2,...)); //delete multiple records
+ *
+ *
+ * For composite primary keys (determined from the table definitions):
+ *
+ * $table->deleteByPk(array($key1,$key2)); //delete 1 record
+ *
+ * //delete multiple records
+ * $table->deleteByPk(array($key1,$key2), array($key3,$key4),...);
+ *
+ * //delete multiple records
+ * $table->deleteByPk(array( array($key1,$key2), array($key3,$key4), .. ));
+ *
+ *
+ * @param mixed primary key values.
+ * @return int number of records deleted.
+ */
+ public function deleteByPk($keys)
+ {
+ if(func_num_args() > 1)
+ $keys = func_get_args();
+ return $this->getCommand()->deleteByPk($keys);
+ }
+
+ /**
+ * Alias for deleteByPk()
+ */
+ public function deleteAllByPks($keys)
+ {
+ if(func_num_args() > 1)
+ $keys = func_get_args();
+ return $this->deleteByPk($keys);
+ }
+
+ /**
+ * Find the number of records.
+ * @param string|TSqlCriteria SQL condition or criteria object.
+ * @param mixed parameter values.
+ * @return int number of records.
+ */
+ public function count($criteria=null,$parameters=array())
+ {
+ $args = func_num_args() > 1 ? array_slice(func_get_args(),1) : null;
+ if($criteria!==null)
+ $criteria = $this->getCriteria($criteria,$parameters, $args);
+ return $this->getCommand()->count($criteria);
+ }
+
+ /**
+ * Updates the table with new name-value pair $data. Each array key must
+ * correspond to a column name in the table. The update condition is
+ * specified by the $where argument and additional binding values can be
+ * specified using the $parameter argument.
+ * This method uses additional arguments as $parameters. E.g.
+ *
+ * $gateway->update($data, 'age > ? AND location = ?', $age, $location);
+ *
+ * @param array new record data.
+ * @param string update condition
+ * @param array additional binding name-value pairs.
+ * @return integer number of records updated.
+ */
+ public function update($data, $criteria, $parameters=array())
+ {
+ $args = func_num_args() > 2 ? array_slice(func_get_args(),2) : null;
+ $criteria = $this->getCriteria($criteria,$parameters, $args);
+ return $this->getCommand()->update($data, $criteria);
+ }
+
+ /**
+ * Inserts a new record into the table. Each array key must
+ * correspond to a column name in the table unless a null value is permitted.
+ * @param array new record data.
+ * @return mixed last insert id if one column contains a serial or sequence,
+ * otherwise true if command executes successfully and affected 1 or more rows.
+ */
+ public function insert($data)
+ {
+ return $this->getCommand()->insert($data);
+ }
+
+ /**
+ * @return mixed last insert id, null if none is found.
+ */
+ public function getLastInsertId()
+ {
+ return $this->getCommand()->getLastInsertId();
+ }
+
+ /**
+ * Create a new TSqlCriteria object from a string $criteria. The $args
+ * are additional parameters and are used in place of the $parameters
+ * if $parameters is not an array and $args is an arrary.
+ * @param string|TSqlCriteria sql criteria
+ * @param mixed parameters passed by the user.
+ * @param array additional parameters obtained from function_get_args().
+ * @return TSqlCriteria criteria object.
+ */
+ protected function getCriteria($criteria, $parameters, $args)
+ {
+ if(is_string($criteria))
+ {
+ $useArgs = !is_array($parameters) && is_array($args);
+ return new TSqlCriteria($criteria,$useArgs ? $args : $parameters);
+ }
+ else if($criteria instanceof TSqlCriteria)
+ return $criteria;
+ else
+ throw new TDbException('dbtablegateway_invalid_criteria');
+ }
+
+ /**
+ * Dynamic find method using parts of method name as search criteria.
+ * Method name starting with "findBy" only returns 1 record.
+ * Method name starting with "findAllBy" returns 0 or more records.
+ * Method name starting with "deleteBy" deletes records by the trail criteria.
+ * The condition is taken as part of the method name after "findBy", "findAllBy"
+ * or "deleteBy".
+ *
+ * The following are equivalent:
+ *
+ * $table->findByName($name)
+ * $table->find('Name = ?', $name);
+ *
+ *
+ * $table->findByUsernameAndPassword($name,$pass); // OR may be used
+ * $table->findBy_Username_And_Password($name,$pass); // _OR_ may be used
+ * $table->find('Username = ? AND Password = ?', $name, $pass);
+ *
+ *
+ * $table->findAllByAge($age);
+ * $table->findAll('Age = ?', $age);
+ *
+ *
+ * $table->deleteAll('Name = ?', $name);
+ * $table->deleteByName($name);
+ *
+ * @return mixed single record if method name starts with "findBy", 0 or more records
+ * if method name starts with "findAllBy"
+ */
+ public function __call($method,$args)
+ {
+ $delete =false;
+ if($findOne = substr(strtolower($method),0,6)==='findby')
+ $condition = $method[6]==='_' ? substr($method,7) : substr($method,6);
+ else if(substr(strtolower($method),0,9)==='findallby')
+ $condition = $method[9]==='_' ? substr($method,10) : substr($method,9);
+ else if($delete = substr(strtolower($method),0,8)==='deleteby')
+ $condition = $method[8]==='_' ? substr($method,9) : substr($method,8);
+ else if($delete = substr(strtolower($method),0,11)==='deleteallby')
+ $condition = $method[11]==='_' ? substr($method,12) : substr($method,11);
+ else
+ return null;
+
+ $criteria = $this->getCommand()->createCriteriaFromString($method, $condition, $args);
+ if($delete)
+ return $this->deleteAll($criteria);
+ else
+ return $findOne ? $this->find($criteria) : $this->findAll($criteria);
+ }
+}
diff --git a/framework/Data/SqlMap/Configuration/TDiscriminator.php b/framework/Data/SqlMap/Configuration/TDiscriminator.php
index cbc05612..f0c9187b 100644
--- a/framework/Data/SqlMap/Configuration/TDiscriminator.php
+++ b/framework/Data/SqlMap/Configuration/TDiscriminator.php
@@ -1,232 +1,232 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * The TDiscriminator corresponds to the tag within a .
- *
- * TDiscriminator allows inheritance logic in SqlMap result mappings.
- * SqlMap compares the data found in the discriminator column to the different
- * values using the column value's string equivalence. When the string values
- * matches a particular , SqlMap will use the defined by
- * {@link resultMapping TSubMap::setResultMapping()} property for loading
- * the object data.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TDiscriminator extends TComponent
-{
- private $_column;
- private $_type;
- private $_typeHandler=null;
- private $_columnIndex;
- private $_nullValue;
- private $_mapping;
- private $_resultMaps=array();
- private $_subMaps=array();
-
- /**
- * @return string the name of the column in the result set from which the
- * value will be used to populate the property.
- */
- public function getColumn()
- {
- return $this->_column;
- }
-
- /**
- * @param string the name of the column in the result set from which the
- * value will be used to populate the property.
- */
- public function setColumn($value)
- {
- $this->_column = $value;
- }
-
- /**
- * @param string property type of the parameter to be set.
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * The type attribute is used to explicitly specify the property type of the
- * parameter to be set. If the attribute type is not set and the framework
- * cannot otherwise determine the type, the type is assumed from the default
- * value of the property.
- * @return string property type of the parameter to be set.
- */
- public function setType($value)
- {
- $this->_type = $value;
- }
-
- /**
- * @return string custom type handler class name (may use namespace).
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string custom type handler class name (may use namespace).
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return int index of the column in the ResultSet
- */
- public function getColumnIndex()
- {
- return $this->_columnIndex;
- }
-
- /**
- * The columnIndex attribute value is the index of the column in the
- * ResultSet from which the value will be used to populate the object property.
- * @param int index of the column in the ResultSet
- */
- public function setColumnIndex($value)
- {
- $this->_columnIndex = TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return mixed outgoing null value replacement.
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * @param mixed outgoing null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- /**
- * @return TResultProperty result property for the discriminator column.
- */
- public function getMapping()
- {
- return $this->_mapping;
- }
-
- /**
- * @param TSubMap add new sub mapping.
- */
- public function addSubMap($subMap)
- {
- $this->_subMaps[] = $subMap;
- }
-
- /**
- * @param string database value
- * @return TResultMap result mapping.
- */
- public function getSubMap($value)
- {
- if(isset($this->_resultMaps[$value]))
- return $this->_resultMaps[$value];
- }
-
- /**
- * Copies the discriminator properties to a new TResultProperty.
- * @param TResultMap result map holding the discriminator.
- */
- public function initMapping($resultMap)
- {
- $this->_mapping = new TResultProperty($resultMap);
- $this->_mapping->setColumn($this->getColumn());
- $this->_mapping->setColumnIndex($this->getColumnIndex());
- $this->_mapping->setType($this->getType());
- $this->_mapping->setTypeHandler($this->getTypeHandler());
- $this->_mapping->setNullValue($this->getNullValue());
- }
-
- /**
- * Set the result maps for particular sub-mapping values.
- * @param TSqlMapManager sql map manager instance.
- */
- public function initialize($manager)
- {
- foreach($this->_subMaps as $subMap)
- {
- $this->_resultMaps[$subMap->getValue()] =
- $manager->getResultMap($subMap->getResultMapping());
- }
- }
-}
-
-/**
- * TSubMap class defines a submapping value and the corresponding
- *
- * The {@link Value setValue()} property is used for comparison with the
- * discriminator column value. When the {@link Value setValue()} matches
- * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping}
- * is used inplace of the current result map.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSubMap extends TComponent
-{
- private $_value;
- private $_resultMapping;
-
- /**
- * @return string value for comparison with discriminator column value.
- */
- public function getValue()
- {
- return $this->_value;
- }
-
- /**
- * @param string value for comparison with discriminator column value.
- */
- public function setValue($value)
- {
- $this->_value = $value;
- }
-
- /**
- * The result map to use when the Value matches the discriminator column value.
- * @return string ID of a result map
- */
- public function getResultMapping()
- {
- return $this->_resultMapping;
- }
-
- /**
- * @param string ID of a result map
- */
- public function setResultMapping($value)
- {
- $this->_resultMapping = $value;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * The TDiscriminator corresponds to the tag within a .
+ *
+ * TDiscriminator allows inheritance logic in SqlMap result mappings.
+ * SqlMap compares the data found in the discriminator column to the different
+ * values using the column value's string equivalence. When the string values
+ * matches a particular , SqlMap will use the defined by
+ * {@link resultMapping TSubMap::setResultMapping()} property for loading
+ * the object data.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TDiscriminator extends TComponent
+{
+ private $_column;
+ private $_type;
+ private $_typeHandler=null;
+ private $_columnIndex;
+ private $_nullValue;
+ private $_mapping;
+ private $_resultMaps=array();
+ private $_subMaps=array();
+
+ /**
+ * @return string the name of the column in the result set from which the
+ * value will be used to populate the property.
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * @param string the name of the column in the result set from which the
+ * value will be used to populate the property.
+ */
+ public function setColumn($value)
+ {
+ $this->_column = $value;
+ }
+
+ /**
+ * @param string property type of the parameter to be set.
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * The type attribute is used to explicitly specify the property type of the
+ * parameter to be set. If the attribute type is not set and the framework
+ * cannot otherwise determine the type, the type is assumed from the default
+ * value of the property.
+ * @return string property type of the parameter to be set.
+ */
+ public function setType($value)
+ {
+ $this->_type = $value;
+ }
+
+ /**
+ * @return string custom type handler class name (may use namespace).
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string custom type handler class name (may use namespace).
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return int index of the column in the ResultSet
+ */
+ public function getColumnIndex()
+ {
+ return $this->_columnIndex;
+ }
+
+ /**
+ * The columnIndex attribute value is the index of the column in the
+ * ResultSet from which the value will be used to populate the object property.
+ * @param int index of the column in the ResultSet
+ */
+ public function setColumnIndex($value)
+ {
+ $this->_columnIndex = TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return mixed outgoing null value replacement.
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * @param mixed outgoing null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+
+ /**
+ * @return TResultProperty result property for the discriminator column.
+ */
+ public function getMapping()
+ {
+ return $this->_mapping;
+ }
+
+ /**
+ * @param TSubMap add new sub mapping.
+ */
+ public function addSubMap($subMap)
+ {
+ $this->_subMaps[] = $subMap;
+ }
+
+ /**
+ * @param string database value
+ * @return TResultMap result mapping.
+ */
+ public function getSubMap($value)
+ {
+ if(isset($this->_resultMaps[$value]))
+ return $this->_resultMaps[$value];
+ }
+
+ /**
+ * Copies the discriminator properties to a new TResultProperty.
+ * @param TResultMap result map holding the discriminator.
+ */
+ public function initMapping($resultMap)
+ {
+ $this->_mapping = new TResultProperty($resultMap);
+ $this->_mapping->setColumn($this->getColumn());
+ $this->_mapping->setColumnIndex($this->getColumnIndex());
+ $this->_mapping->setType($this->getType());
+ $this->_mapping->setTypeHandler($this->getTypeHandler());
+ $this->_mapping->setNullValue($this->getNullValue());
+ }
+
+ /**
+ * Set the result maps for particular sub-mapping values.
+ * @param TSqlMapManager sql map manager instance.
+ */
+ public function initialize($manager)
+ {
+ foreach($this->_subMaps as $subMap)
+ {
+ $this->_resultMaps[$subMap->getValue()] =
+ $manager->getResultMap($subMap->getResultMapping());
+ }
+ }
+}
+
+/**
+ * TSubMap class defines a submapping value and the corresponding
+ *
+ * The {@link Value setValue()} property is used for comparison with the
+ * discriminator column value. When the {@link Value setValue()} matches
+ * that of the discriminator column value, the corresponding {@link ResultMapping setResultMapping}
+ * is used inplace of the current result map.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSubMap extends TComponent
+{
+ private $_value;
+ private $_resultMapping;
+
+ /**
+ * @return string value for comparison with discriminator column value.
+ */
+ public function getValue()
+ {
+ return $this->_value;
+ }
+
+ /**
+ * @param string value for comparison with discriminator column value.
+ */
+ public function setValue($value)
+ {
+ $this->_value = $value;
+ }
+
+ /**
+ * The result map to use when the Value matches the discriminator column value.
+ * @return string ID of a result map
+ */
+ public function getResultMapping()
+ {
+ return $this->_resultMapping;
+ }
+
+ /**
+ * @param string ID of a result map
+ */
+ public function setResultMapping($value)
+ {
+ $this->_resultMapping = $value;
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php
index b78a235c..914d7eb7 100644
--- a/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php
+++ b/framework/Data/SqlMap/Configuration/TInlineParameterMapParser.php
@@ -1,79 +1,79 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TInlineParameterMapParser class.
- *
- * The inline parameter map syntax lets you embed the property name,
- * the property type, the column type, and a null value replacement into a
- * parametrized SQL statement.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TInlineParameterMapParser
-{
- /**
- * Regular expression for parsing inline parameter maps.
- */
- const PARAMETER_TOKEN_REGEXP = '/#([^#]+)#/';
-
- /**
- * Parse the sql text for inline parameters.
- * @param string sql text
- * @param array file and node details for exception message.
- * @return array 'sql' and 'parameters' name value pairs.
- */
- public function parse($sqlText, $scope)
- {
- $matches = array();
- $mappings = array();
- preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
-
- for($i = 0, $k=count($matches[1]); $i<$k; $i++)
- {
- $mappings[] = $this->parseMapping($matches[1][$i], $scope);
- $sqlText = str_replace($matches[0][$i], '?', $sqlText);
- }
- return array('sql'=>$sqlText, 'parameters'=>$mappings);
- }
-
- /**
- * Parse inline parameter with syntax as
- * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string#
- * @param string parameter token
- * @param array file and node details for exception message.
- */
- protected function parseMapping($token, $scope)
- {
- $mapping = new TParameterProperty;
- $properties = explode(',', $token);
- $mapping->setProperty(trim(array_shift($properties)));
- foreach($properties as $property)
- {
- $prop = explode('=',$property);
- $name = trim($prop[0]); $value=trim($prop[1]);
- if($mapping->canSetProperty($name))
- $mapping->{'set'.$name}($value);
- else
- {
- throw new TSqlMapUndefinedException(
- 'sqlmap_undefined_property_inline_map',
- $name, $scope['file'], $scope['node'], $token);
- }
- }
- return $mapping;
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TInlineParameterMapParser class.
+ *
+ * The inline parameter map syntax lets you embed the property name,
+ * the property type, the column type, and a null value replacement into a
+ * parametrized SQL statement.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TInlineParameterMapParser
+{
+ /**
+ * Regular expression for parsing inline parameter maps.
+ */
+ const PARAMETER_TOKEN_REGEXP = '/#([^#]+)#/';
+
+ /**
+ * Parse the sql text for inline parameters.
+ * @param string sql text
+ * @param array file and node details for exception message.
+ * @return array 'sql' and 'parameters' name value pairs.
+ */
+ public function parse($sqlText, $scope)
+ {
+ $matches = array();
+ $mappings = array();
+ preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
+
+ for($i = 0, $k=count($matches[1]); $i<$k; $i++)
+ {
+ $mappings[] = $this->parseMapping($matches[1][$i], $scope);
+ $sqlText = str_replace($matches[0][$i], '?', $sqlText);
+ }
+ return array('sql'=>$sqlText, 'parameters'=>$mappings);
+ }
+
+ /**
+ * Parse inline parameter with syntax as
+ * #propertyName,type=string,dbype=Varchar,nullValue=N/A,handler=string#
+ * @param string parameter token
+ * @param array file and node details for exception message.
+ */
+ protected function parseMapping($token, $scope)
+ {
+ $mapping = new TParameterProperty;
+ $properties = explode(',', $token);
+ $mapping->setProperty(trim(array_shift($properties)));
+ foreach($properties as $property)
+ {
+ $prop = explode('=',$property);
+ $name = trim($prop[0]); $value=trim($prop[1]);
+ if($mapping->canSetProperty($name))
+ $mapping->{'set'.$name}($value);
+ else
+ {
+ throw new TSqlMapUndefinedException(
+ 'sqlmap_undefined_property_inline_map',
+ $name, $scope['file'], $scope['node'], $token);
+ }
+ }
+ return $mapping;
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TParameterMap.php b/framework/Data/SqlMap/Configuration/TParameterMap.php
index d7cc5eb6..ee740fdb 100644
--- a/framework/Data/SqlMap/Configuration/TParameterMap.php
+++ b/framework/Data/SqlMap/Configuration/TParameterMap.php
@@ -1,210 +1,210 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TParameterMap corresponds to the element.
- *
- * TParameterMap holds one or more parameter child elements that map object
- * properties to placeholders in a SQL statement.
- *
- * A TParameterMap defines an ordered list of values that match up with the
- * placeholders of a parameterized query statement. While the attributes
- * specified by the map still need to be in the correct order, each parameter
- * is named. You can populate the underlying class in any order, and the
- * TParameterMap ensures each value is passed in the correct order.
- *
- * Parameter Maps can be provided as an external element and inline.
- * The element accepts two attributes: id (required) and extends (optional).
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TParameterMap extends TComponent
-{
- private $_extend;
- private $_properties;
- private $_propertyMap;
- private $_extendMap;
- private $_ID;
-
- /**
- * Initialize the properties and property map collections.
- */
- public function __construct()
- {
- $this->_properties = new TList;
- $this->_propertyMap = new TMap;
- }
-
- /**
- * @return string a unique identifier for the .
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string a unique identifier for the .
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return TParameterProperty[] list of properties for the parameter map.
- */
- public function getProperties()
- {
- return $this->_properties;
- }
-
- /**
- * @return string name of another upon which to base this TParameterMap.
- */
- public function getExtends()
- {
- return $this->_extend;
- }
-
- /**
- * @param string name of another upon which to base this TParameterMap.
- */
- public function setExtends($value)
- {
- $this->_extend = $value;
- }
-
- /**
- * @param string name of a parameter property.
- * @return TParameterProperty parameter property.
- * @throws TSqlMapException if index is not string nor integer.
- */
- public function getProperty($index)
- {
- if(is_string($index))
- return $this->_propertyMap->itemAt($index);
- else if(is_int($index))
- return $this->_properties->itemAt($index);
- else
- throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index);
- }
-
- /**
- * @param TParameterProperty new parameter property
- */
- public function addProperty(TParameterProperty $property)
- {
- $this->_propertyMap->add($property->getProperty(), $property);
- $this->_properties->add($property);
- }
-
- /**
- * @param int parameter property index
- * @param TParameterProperty new parameter property.
- */
- public function insertProperty($index, TParameterProperty $property)
- {
- $this->_propertyMap->add($property->getProperty(), $property);
- $this->_properties->insertAt($index, $property);
- }
-
- /**
- * @return array list of property names.
- */
- public function getPropertyNames()
- {
- return $this->_propertyMap->getKeys();
- }
-
- /**
- * Get the value of a property from the the parameter object.
- * @param TSqlMapTypeHandlerRegistry type handler registry.
- * @param TParameterProperty parameter proproperty.
- * @param mixed parameter object to get the value from.
- * @return unknown
- */
- public function getPropertyValue($registry, $property, $parameterValue)
- {
- $value = $this->getObjectValue($parameterValue,$property);
-
- if(($handler=$this->createTypeHandler($property, $registry))!==null)
- $value = $handler->getParameter($value);
-
- $value = $this->nullifyDefaultValue($property,$value);
-
- if(($type = $property->getType())!==null)
- $value = $registry->convertToType($type, $value);
-
- return $value;
- }
-
-
- /**
- * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
- * @param TParameterProperty parameter property
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return TSqlMapTypeHandler type handler.
- */
- protected function createTypeHandler($property, $registry)
- {
- $type=$property->getTypeHandler() ? $property->getTypeHandler() : $property->getType();
- $handler=$registry->getTypeHandler($type);
- if($handler===null && $property->getTypeHandler())
- $handler = Prado::createComponent($type);
- return $handler;
- }
-
-
- /**
- * @param mixed object to obtain the property from.
- * @param TParameterProperty parameter property.
- * @return mixed property value.
- * @throws TSqlMapException if property access is invalid.
- */
- protected function getObjectValue($object,$property)
- {
- try
- {
- return TPropertyAccess::get($object, $property->getProperty());
- }
- catch (TInvalidPropertyException $e)
- {
- throw new TSqlMapException(
- 'sqlmap_unable_to_get_property_for_parameter',
- $this->getID(),
- $property->getProperty(),
- (is_object($object) ? get_class($object) : gettype($object))
- );
- }
- }
-
- /**
- * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()},
- * set the current value to null.
- * @param TParameterProperty parameter property.
- * @param mixed current property value
- * @return mixed null if NullValue matches currrent value.
- */
- protected function nullifyDefaultValue($property,$value)
- {
- if(($nullValue = $property->getNullValue())!==null)
- {
- if($nullValue === $value)
- $value = null;
- }
- return $value;
- }
-}
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TParameterMap corresponds to the element.
+ *
+ * TParameterMap holds one or more parameter child elements that map object
+ * properties to placeholders in a SQL statement.
+ *
+ * A TParameterMap defines an ordered list of values that match up with the
+ * placeholders of a parameterized query statement. While the attributes
+ * specified by the map still need to be in the correct order, each parameter
+ * is named. You can populate the underlying class in any order, and the
+ * TParameterMap ensures each value is passed in the correct order.
+ *
+ * Parameter Maps can be provided as an external element and inline.
+ * The element accepts two attributes: id (required) and extends (optional).
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TParameterMap extends TComponent
+{
+ private $_extend;
+ private $_properties;
+ private $_propertyMap;
+ private $_extendMap;
+ private $_ID;
+
+ /**
+ * Initialize the properties and property map collections.
+ */
+ public function __construct()
+ {
+ $this->_properties = new TList;
+ $this->_propertyMap = new TMap;
+ }
+
+ /**
+ * @return string a unique identifier for the .
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string a unique identifier for the .
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return TParameterProperty[] list of properties for the parameter map.
+ */
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+ /**
+ * @return string name of another upon which to base this TParameterMap.
+ */
+ public function getExtends()
+ {
+ return $this->_extend;
+ }
+
+ /**
+ * @param string name of another upon which to base this TParameterMap.
+ */
+ public function setExtends($value)
+ {
+ $this->_extend = $value;
+ }
+
+ /**
+ * @param string name of a parameter property.
+ * @return TParameterProperty parameter property.
+ * @throws TSqlMapException if index is not string nor integer.
+ */
+ public function getProperty($index)
+ {
+ if(is_string($index))
+ return $this->_propertyMap->itemAt($index);
+ else if(is_int($index))
+ return $this->_properties->itemAt($index);
+ else
+ throw new TSqlMapException('sqlmap_index_must_be_string_or_int', $index);
+ }
+
+ /**
+ * @param TParameterProperty new parameter property
+ */
+ public function addProperty(TParameterProperty $property)
+ {
+ $this->_propertyMap->add($property->getProperty(), $property);
+ $this->_properties->add($property);
+ }
+
+ /**
+ * @param int parameter property index
+ * @param TParameterProperty new parameter property.
+ */
+ public function insertProperty($index, TParameterProperty $property)
+ {
+ $this->_propertyMap->add($property->getProperty(), $property);
+ $this->_properties->insertAt($index, $property);
+ }
+
+ /**
+ * @return array list of property names.
+ */
+ public function getPropertyNames()
+ {
+ return $this->_propertyMap->getKeys();
+ }
+
+ /**
+ * Get the value of a property from the the parameter object.
+ * @param TSqlMapTypeHandlerRegistry type handler registry.
+ * @param TParameterProperty parameter proproperty.
+ * @param mixed parameter object to get the value from.
+ * @return unknown
+ */
+ public function getPropertyValue($registry, $property, $parameterValue)
+ {
+ $value = $this->getObjectValue($parameterValue,$property);
+
+ if(($handler=$this->createTypeHandler($property, $registry))!==null)
+ $value = $handler->getParameter($value);
+
+ $value = $this->nullifyDefaultValue($property,$value);
+
+ if(($type = $property->getType())!==null)
+ $value = $registry->convertToType($type, $value);
+
+ return $value;
+ }
+
+
+ /**
+ * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
+ * @param TParameterProperty parameter property
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @return TSqlMapTypeHandler type handler.
+ */
+ protected function createTypeHandler($property, $registry)
+ {
+ $type=$property->getTypeHandler() ? $property->getTypeHandler() : $property->getType();
+ $handler=$registry->getTypeHandler($type);
+ if($handler===null && $property->getTypeHandler())
+ $handler = Prado::createComponent($type);
+ return $handler;
+ }
+
+
+ /**
+ * @param mixed object to obtain the property from.
+ * @param TParameterProperty parameter property.
+ * @return mixed property value.
+ * @throws TSqlMapException if property access is invalid.
+ */
+ protected function getObjectValue($object,$property)
+ {
+ try
+ {
+ return TPropertyAccess::get($object, $property->getProperty());
+ }
+ catch (TInvalidPropertyException $e)
+ {
+ throw new TSqlMapException(
+ 'sqlmap_unable_to_get_property_for_parameter',
+ $this->getID(),
+ $property->getProperty(),
+ (is_object($object) ? get_class($object) : gettype($object))
+ );
+ }
+ }
+
+ /**
+ * When the actual value matches the {@link NullValue TParameterProperty::setNullValue()},
+ * set the current value to null.
+ * @param TParameterProperty parameter property.
+ * @param mixed current property value
+ * @return mixed null if NullValue matches currrent value.
+ */
+ protected function nullifyDefaultValue($property,$value)
+ {
+ if(($nullValue = $property->getNullValue())!==null)
+ {
+ if($nullValue === $value)
+ $value = null;
+ }
+ return $value;
+ }
+}
diff --git a/framework/Data/SqlMap/Configuration/TParameterProperty.php b/framework/Data/SqlMap/Configuration/TParameterProperty.php
index d941ca18..a79af7f2 100644
--- a/framework/Data/SqlMap/Configuration/TParameterProperty.php
+++ b/framework/Data/SqlMap/Configuration/TParameterProperty.php
@@ -1,150 +1,150 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TParameterProperty corresponds to the tag and defines
- * one object property for the
- *
- * The {@link NullValue setNullValue()} attribute can be set to any valid
- * value (based on property type). The {@link NullValue setNullValue()} attribute
- * is used to specify an inbound null value replacement. What this means is
- * that when the value is detected in the object property, a NULL will be written
- * to the database (the opposite behavior of an inbound null value replacement).
- * This allows you to use a magic null number in your application for types that
- * do not support null values (such as int, double, float). When these types of
- * properties contain a matching null value (for example, say, -9999), a NULL
- * will be written to the database instead of the value.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TParameterProperty extends TComponent
-{
- private $_typeHandler;
- private $_type;
- private $_column;
- private $_dbType;
- private $_property;
- private $_nullValue;
-
- /**
- * @return string class name of a custom type handler.
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string class name of a custom type handler.
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return string type of the parameter's property
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * @param string type of the parameter's property
- */
- public function setType($value)
- {
- $this->_type = $value;
- }
-
- /**
- * @return string name of a parameter to be used in the SQL statement.
- */
- public function getColumn()
- {
- return $this->_column;
- }
-
- /**
- * @param string name of a parameter to be used in the SQL statement.
- */
- public function setColumn($value)
- {
- $this->_column = $value;
- }
-
- /**
- * @return string the database column type of the parameter to be set by this property.
- */
- public function getDbType()
- {
- return $this->_dbType;
- }
-
- /**
- * @param string the database column type of the parameter to be set by this property.
- */
- public function setDbType($value)
- {
- $this->_dbType = $value;
- }
-
- /**
- * @return string name of a property of the parameter object.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @param string name of a property of the parameter object.
- */
- public function setProperty($value)
- {
- $this->_property = $value;
- }
-
- /**
- * @return mixed null value replacement
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * The nullValue attribute is used to specify an outgoing null value replacement.
- * @param mixed null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = 'TParameterProperty';
- if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
- if ($this->_type===null) $exprops[] = "\0$cn\0_type";
- if ($this->_column===null) $exprops[] = "\0$cn\0_column";
- if ($this->_dbType===null) $exprops[] = "\0$cn\0_dbType";
- if ($this->_property===null) $exprops[] = "\0$cn\0_property";
- if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TParameterProperty corresponds to the tag and defines
+ * one object property for the
+ *
+ * The {@link NullValue setNullValue()} attribute can be set to any valid
+ * value (based on property type). The {@link NullValue setNullValue()} attribute
+ * is used to specify an inbound null value replacement. What this means is
+ * that when the value is detected in the object property, a NULL will be written
+ * to the database (the opposite behavior of an inbound null value replacement).
+ * This allows you to use a magic null number in your application for types that
+ * do not support null values (such as int, double, float). When these types of
+ * properties contain a matching null value (for example, say, -9999), a NULL
+ * will be written to the database instead of the value.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TParameterProperty extends TComponent
+{
+ private $_typeHandler;
+ private $_type;
+ private $_column;
+ private $_dbType;
+ private $_property;
+ private $_nullValue;
+
+ /**
+ * @return string class name of a custom type handler.
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string class name of a custom type handler.
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return string type of the parameter's property
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * @param string type of the parameter's property
+ */
+ public function setType($value)
+ {
+ $this->_type = $value;
+ }
+
+ /**
+ * @return string name of a parameter to be used in the SQL statement.
+ */
+ public function getColumn()
+ {
+ return $this->_column;
+ }
+
+ /**
+ * @param string name of a parameter to be used in the SQL statement.
+ */
+ public function setColumn($value)
+ {
+ $this->_column = $value;
+ }
+
+ /**
+ * @return string the database column type of the parameter to be set by this property.
+ */
+ public function getDbType()
+ {
+ return $this->_dbType;
+ }
+
+ /**
+ * @param string the database column type of the parameter to be set by this property.
+ */
+ public function setDbType($value)
+ {
+ $this->_dbType = $value;
+ }
+
+ /**
+ * @return string name of a property of the parameter object.
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @param string name of a property of the parameter object.
+ */
+ public function setProperty($value)
+ {
+ $this->_property = $value;
+ }
+
+ /**
+ * @return mixed null value replacement
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * The nullValue attribute is used to specify an outgoing null value replacement.
+ * @param mixed null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+
+ public function __sleep()
+ {
+ $exprops = array(); $cn = 'TParameterProperty';
+ if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
+ if ($this->_type===null) $exprops[] = "\0$cn\0_type";
+ if ($this->_column===null) $exprops[] = "\0$cn\0_column";
+ if ($this->_dbType===null) $exprops[] = "\0$cn\0_dbType";
+ if ($this->_property===null) $exprops[] = "\0$cn\0_property";
+ if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
+ return array_diff(parent::__sleep(),$exprops);
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TResultMap.php b/framework/Data/SqlMap/Configuration/TResultMap.php
index e05c4c47..77b5f0b2 100644
--- a/framework/Data/SqlMap/Configuration/TResultMap.php
+++ b/framework/Data/SqlMap/Configuration/TResultMap.php
@@ -1,200 +1,200 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TResultMap corresponds to mapping tag.
- *
- * A TResultMap lets you control how data is extracted from the result of a
- * query, and how the columns are mapped to object properties. A TResultMap
- * can describe the column type, a null value replacement, and complex property
- * mappings including Collections.
- *
- * The can contain any number of property mappings that map object
- * properties to the columns of a result element. The property mappings are
- * applied, and the columns are read, in the order that they are defined.
- * Maintaining the element order ensures consistent results between different
- * drivers and providers.
- *
- * The {@link Class setClass()} property must be a PHP class object or array instance.
- *
- * The optional {@link Extends setExtends()} attribute can be set to the ID of
- * another upon which to base this . All properties of the
- * "parent" will be included as part of this , and values
- * from the "parent" are set before any values specified by this .
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TResultMap extends TComponent
-{
- private $_columns;
- private $_class;
- private $_extends;
- private $_groupBy;
- private $_discriminator;
- private $_typeHandlers;
- private $_ID;
-
- /**
- * Initialize the columns collection.
- */
- public function __construct()
- {
- $this->_columns=new TMap;
- }
-
- /**
- * @return string a unique identifier for the .
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string a unique identifier for the .
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return string result class name.
- */
- public function getClass()
- {
- return $this->_class;
- }
-
- /**
- * @param string result class name.
- */
- public function setClass($value)
- {
- $this->_class = $value;
- }
-
- /**
- * @return TMap result columns.
- */
- public function getColumns()
- {
- return $this->_columns;
- }
-
- /**
- * @return string result map extends another result map.
- */
- public function getExtends()
- {
- return $this->_extends;
- }
-
- /**
- * @param string result map extends another result map.
- */
- public function setExtends($value)
- {
- $this->_extends = $value;
- }
-
- /**
- * @return string result map groups by.
- */
- public function getGroupBy()
- {
- return $this->_groupBy;
- }
-
- /**
- * @param string result map group by
- */
- public function setGroupBy($value)
- {
- $this->_groupBy = $value;
- }
-
- /**
- * @return TDiscriminator result class discriminator.
- */
- public function getDiscriminator()
- {
- return $this->_discriminator;
- }
-
- /**
- * @param TDiscriminator result class discriminator.
- */
- public function setDiscriminator(TDiscriminator $value)
- {
- $this->_discriminator = $value;
- }
-
- /**
- * Add a TResultProperty to result mapping.
- * @param TResultProperty result property.
- */
- public function addResultProperty(TResultProperty $property)
- {
- $this->_columns[$property->getProperty()] = $property;
- }
-
- /**
- * Create a new instance of the class of this result map.
- * @param TSqlMapTypeHandlerRegistry type handler registry.
- * @return mixed new result object.
- * @throws TSqlMapException
- */
- public function createInstanceOfResult($registry)
- {
- $handler = $registry->getTypeHandler($this->getClass());
- try
- {
- if($handler!==null)
- return $handler->createNewInstance();
- else
- return $registry->createInstanceOf($this->getClass());
- }
- catch (TSqlMapException $e)
- {
- throw new TSqlMapException(
- 'sqlmap_unable_to_create_new_instance',
- $this->getClass(), get_class($handler), $this->getID());
- }
- }
-
- /**
- * Result sub-mappings using the discriminiator column.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array row data.
- * @return TResultMap result sub-map.
- */
- public function resolveSubMap($registry,$row)
- {
- $subMap = $this;
- if(($disc = $this->getDiscriminator())!==null)
- {
- $value = $disc->getMapping()->getPropertyValue($registry,$row);
- $subMap = $disc->getSubMap((string)$value);
-
- if($subMap===null)
- $subMap = $this;
- else if($subMap !== $this)
- $subMap = $subMap->resolveSubMap($registry,$row);
- }
- return $subMap;
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TResultMap corresponds to mapping tag.
+ *
+ * A TResultMap lets you control how data is extracted from the result of a
+ * query, and how the columns are mapped to object properties. A TResultMap
+ * can describe the column type, a null value replacement, and complex property
+ * mappings including Collections.
+ *
+ * The can contain any number of property mappings that map object
+ * properties to the columns of a result element. The property mappings are
+ * applied, and the columns are read, in the order that they are defined.
+ * Maintaining the element order ensures consistent results between different
+ * drivers and providers.
+ *
+ * The {@link Class setClass()} property must be a PHP class object or array instance.
+ *
+ * The optional {@link Extends setExtends()} attribute can be set to the ID of
+ * another upon which to base this . All properties of the
+ * "parent" will be included as part of this , and values
+ * from the "parent" are set before any values specified by this .
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TResultMap extends TComponent
+{
+ private $_columns;
+ private $_class;
+ private $_extends;
+ private $_groupBy;
+ private $_discriminator;
+ private $_typeHandlers;
+ private $_ID;
+
+ /**
+ * Initialize the columns collection.
+ */
+ public function __construct()
+ {
+ $this->_columns=new TMap;
+ }
+
+ /**
+ * @return string a unique identifier for the .
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string a unique identifier for the .
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return string result class name.
+ */
+ public function getClass()
+ {
+ return $this->_class;
+ }
+
+ /**
+ * @param string result class name.
+ */
+ public function setClass($value)
+ {
+ $this->_class = $value;
+ }
+
+ /**
+ * @return TMap result columns.
+ */
+ public function getColumns()
+ {
+ return $this->_columns;
+ }
+
+ /**
+ * @return string result map extends another result map.
+ */
+ public function getExtends()
+ {
+ return $this->_extends;
+ }
+
+ /**
+ * @param string result map extends another result map.
+ */
+ public function setExtends($value)
+ {
+ $this->_extends = $value;
+ }
+
+ /**
+ * @return string result map groups by.
+ */
+ public function getGroupBy()
+ {
+ return $this->_groupBy;
+ }
+
+ /**
+ * @param string result map group by
+ */
+ public function setGroupBy($value)
+ {
+ $this->_groupBy = $value;
+ }
+
+ /**
+ * @return TDiscriminator result class discriminator.
+ */
+ public function getDiscriminator()
+ {
+ return $this->_discriminator;
+ }
+
+ /**
+ * @param TDiscriminator result class discriminator.
+ */
+ public function setDiscriminator(TDiscriminator $value)
+ {
+ $this->_discriminator = $value;
+ }
+
+ /**
+ * Add a TResultProperty to result mapping.
+ * @param TResultProperty result property.
+ */
+ public function addResultProperty(TResultProperty $property)
+ {
+ $this->_columns[$property->getProperty()] = $property;
+ }
+
+ /**
+ * Create a new instance of the class of this result map.
+ * @param TSqlMapTypeHandlerRegistry type handler registry.
+ * @return mixed new result object.
+ * @throws TSqlMapException
+ */
+ public function createInstanceOfResult($registry)
+ {
+ $handler = $registry->getTypeHandler($this->getClass());
+ try
+ {
+ if($handler!==null)
+ return $handler->createNewInstance();
+ else
+ return $registry->createInstanceOf($this->getClass());
+ }
+ catch (TSqlMapException $e)
+ {
+ throw new TSqlMapException(
+ 'sqlmap_unable_to_create_new_instance',
+ $this->getClass(), get_class($handler), $this->getID());
+ }
+ }
+
+ /**
+ * Result sub-mappings using the discriminiator column.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array row data.
+ * @return TResultMap result sub-map.
+ */
+ public function resolveSubMap($registry,$row)
+ {
+ $subMap = $this;
+ if(($disc = $this->getDiscriminator())!==null)
+ {
+ $value = $disc->getMapping()->getPropertyValue($registry,$row);
+ $subMap = $disc->getSubMap((string)$value);
+
+ if($subMap===null)
+ $subMap = $this;
+ else if($subMap !== $this)
+ $subMap = $subMap->resolveSubMap($registry,$row);
+ }
+ return $subMap;
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TResultProperty.php b/framework/Data/SqlMap/Configuration/TResultProperty.php
index 0a016350..8447d400 100644
--- a/framework/Data/SqlMap/Configuration/TResultProperty.php
+++ b/framework/Data/SqlMap/Configuration/TResultProperty.php
@@ -1,344 +1,344 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TResultProperty corresponds a tags inside a tag.
- *
- * The {@link NullValue setNullValue()} attribute can be set to any valid
- * value (based on property type). The {@link NullValue setNullValue()} attribute
- * is used to specify an outgoing null value replacement. What this means is
- * that when a null value is detected in the result, the corresponding value of
- * the {@link NullValue getNullValue()} will be used instead.
- *
- * The {@link Select setSelect()} property is used to describe a relationship
- * between objects and to automatically load complex (i.e. user defined)
- * property types. The value of the {@link Select setSelect()} property must be
- * the name of another mapped statement. The value of the database
- * {@link Column setColumn()} that is defined in the same property element as
- * this statement attribute will be passed to the related mapped statement as
- * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified
- * with the {@link Select setSelect()} .
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TResultProperty extends TComponent
-{
- private $_nullValue;
- private $_propertyName;
- private $_columnName;
- private $_columnIndex=-1;
- private $_nestedResultMapName;
- private $_nestedResultMap;
- private $_valueType;
- private $_typeHandler;
- private $_isLazyLoad=false;
- private $_select;
-
- private $_hostResultMapID='inplicit internal mapping';
-
- const LIST_TYPE = 0;
- const ARRAY_TYPE = 1;
-
- /**
- * Gets the containing result map ID.
- * @param TResultMap containing result map.
- */
- public function __construct($resultMap=null)
- {
- if($resultMap instanceof TResultMap)
- $this->_hostResultMapID = $resultMap->getID();
- }
-
- /**
- * @return mixed null value replacement.
- */
- public function getNullValue()
- {
- return $this->_nullValue;
- }
-
- /**
- * @param mixed null value replacement.
- */
- public function setNullValue($value)
- {
- $this->_nullValue = $value;
- }
-
- /**
- * @return string name of a property of the result object that will be set to.
- */
- public function getProperty()
- {
- return $this->_propertyName;
- }
-
- /**
- * @param string name of a property of the result object that will be set to.
- */
- public function setProperty($value)
- {
- $this->_propertyName = $value;
- }
-
- /**
- * @return string name of the column in the result set from which the value
- * will be used to populate the property.
- */
- public function getColumn()
- {
- return $this->_columnName;
- }
-
- /**
- * @param string name of the column in the result set from which the value
- * will be used to populate the property.
- */
- public function setColumn($value)
- {
- $this->_columnName = $value;
- }
-
- /**
- * @return int index of the column in the ResultSet from which the value will
- * be used to populate the object property
- */
- public function getColumnIndex()
- {
- return $this->_columnIndex;
- }
-
- /**
- * @param int index of the column in the ResultSet from which the value will
- * be used to populate the object property
- */
- public function setColumnIndex($value)
- {
- $this->_columnIndex = TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return string ID of another used to fill the property.
- */
- public function getResultMapping()
- {
- return $this->_nestedResultMapName;
- }
-
- /**
- * @param string ID of another used to fill the property.
- */
- public function setResultMapping($value)
- {
- $this->_nestedResultMapName = $value;
- }
-
- /**
- * @return TResultMap nested result map.
- */
- public function getNestedResultMap()
- {
- return $this->_nestedResultMap;
- }
-
- /**
- * @param TResult nested result map.
- */
- public function setNestedResultMap($value)
- {
- $this->_nestedResultMap = $value;
- }
-
- /**
- * @return string property type of the object property to be set.
- */
- public function getType()
- {
- return $this->_valueType;
- }
-
- /**
- * @param string property type of the object property to be set.
- */
- public function setType($value)
- {
- $this->_valueType = $value;
- }
-
- /**
- * @return string custom type handler class name (may use namespace).
- */
- public function getTypeHandler()
- {
- return $this->_typeHandler;
- }
-
- /**
- * @param string custom type handler class name (may use namespace).
- */
- public function setTypeHandler($value)
- {
- $this->_typeHandler = $value;
- }
-
- /**
- * @return string name of another mapped statement
- */
- public function getSelect()
- {
- return $this->_select;
- }
-
- /**
- * The select property is used to describe a relationship between objects
- * and to automatically load complex (i.e. user defined) property types.
- * @param string name of another mapped statement.
- */
- public function setSelect($value)
- {
- $this->_select = $value;
- }
-
- /**
- * @return boolean indicate whether or not the select statement's results should be lazy loaded
- */
- public function getLazyLoad()
- {
- return $this->_isLazyLoad;
- }
-
- /**
- * @param boolean indicate whether or not the select statement's results should be lazy loaded
- */
- public function setLazyLoad($value)
- {
- $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false);
- }
-
- /**
- * Gets the value for the current property, converts to applicable type if necessary.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array result row
- * @return mixed property value.
- */
- public function getPropertyValue($registry,$row)
- {
- $value = null;
- $index = $this->getColumnIndex();
- $name = $this->getColumn();
- if($index > 0 && isset($row[$index]))
- $value = $this->getTypedValue($registry,$row[$index]);
- else if(isset($row[$name]))
- $value = $this->getTypedValue($registry,$row[$name]);
- if(($value===null) && ($this->getNullValue()!==null))
- $value = $this->getTypedValue($registry,$this->getNullValue());
- return $value;
- }
-
- /**
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param mixed raw property value
- * @return mixed property value casted to specific type.
- */
- protected function getTypedValue($registry,$value)
- {
- if(($handler = $this->createTypeHandler($registry))!==null)
- return $handler->getResult($value);
- else
- return $registry->convertToType($this->getType(), $value);
- }
-
- /**
- * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return TSqlMapTypeHandler type handler.
- */
- protected function createTypeHandler($registry)
- {
- $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType();
- $handler=$registry->getTypeHandler($type);
- if($handler===null && $this->getTypeHandler())
- $handler = Prado::createComponent($type);
- return $handler;
- }
-
- /**
- * Determines if the type is an instance of ArrayAccess, TList or an array.
- * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE
- */
- protected function getPropertyValueType()
- {
- if(class_exists($type = $this->getType(), false)) //NO force autoloading
- {
- if($type==='TList')
- return self::LIST_TYPE;
- $class = new ReflectionClass($type);
- if($class->isSubclassOf('TList'))
- return self::LIST_TYPE;
- if($class->implementsInterface('ArrayAccess'))
- return self::ARRAY_TYPE;
- }
- if(strtolower($type) == 'array')
- return self::ARRAY_TYPE;
- }
-
- /**
- * Returns true if the result property {@link Type getType()} is of TList type
- * or that the actual result object is an instance of TList.
- * @param object result object
- * @return boolean true if the result object is an instance of TList
- */
- public function instanceOfListType($target)
- {
- if($this->getType()===null)
- return TPropertyAccess::get($target,$this->getProperty()) instanceof TList;
- return $this->getPropertyValueType() == self::LIST_TYPE;
- }
-
- /**
- * Returns true if the result property {@link Type getType()} is of ArrayAccess
- * or that the actual result object is an array or implements ArrayAccess
- * @param object result object
- * @return boolean true if the result object is an instance of ArrayAccess or is an array.
- */
- public function instanceOfArrayType($target)
- {
- if($this->getType()===null)
- {
- $prop = TPropertyAccess::get($target,$this->getProperty());
- if(is_object($prop))
- return $prop instanceof ArrayAccess;
- return is_array($prop);
- }
- return $this->getPropertyValueType() == self::ARRAY_TYPE;
- }
-
- public function __sleep()
- {
- $exprops = array(); $cn = 'TResultProperty';
- if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
- if ($this->_propertyName===null) $exprops[] = "\0$cn\0_propertyNama";
- if ($this->_columnName===null) $exprops[] = "\0$cn\0_columnName";
- if ($this->_columnIndex==-1) $exprops[] = "\0$cn\0_columnIndex";
- if ($this->_nestedResultMapName===null) $exprops[] = "\0$cn\0_nestedResultMapName";
- if ($this->_nestedResultMap===null) $exprops[] = "\0$cn\0_nestedResultMap";
- if ($this->_valueType===null) $exprops[] = "\0$cn\0_valueType";
- if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
- if ($this->_isLazyLoad===false) $exprops[] = "\0$cn\0_isLazyLoad";
- if ($this->_select===null) $exprops[] = "\0$cn\0_select";
- return array_diff(parent::__sleep(),$exprops);
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TResultProperty corresponds a tags inside a tag.
+ *
+ * The {@link NullValue setNullValue()} attribute can be set to any valid
+ * value (based on property type). The {@link NullValue setNullValue()} attribute
+ * is used to specify an outgoing null value replacement. What this means is
+ * that when a null value is detected in the result, the corresponding value of
+ * the {@link NullValue getNullValue()} will be used instead.
+ *
+ * The {@link Select setSelect()} property is used to describe a relationship
+ * between objects and to automatically load complex (i.e. user defined)
+ * property types. The value of the {@link Select setSelect()} property must be
+ * the name of another mapped statement. The value of the database
+ * {@link Column setColumn()} that is defined in the same property element as
+ * this statement attribute will be passed to the related mapped statement as
+ * the parameter. The {@link LazyLoad setLayLoad()} attribute can be specified
+ * with the {@link Select setSelect()} .
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TResultProperty extends TComponent
+{
+ private $_nullValue;
+ private $_propertyName;
+ private $_columnName;
+ private $_columnIndex=-1;
+ private $_nestedResultMapName;
+ private $_nestedResultMap;
+ private $_valueType;
+ private $_typeHandler;
+ private $_isLazyLoad=false;
+ private $_select;
+
+ private $_hostResultMapID='inplicit internal mapping';
+
+ const LIST_TYPE = 0;
+ const ARRAY_TYPE = 1;
+
+ /**
+ * Gets the containing result map ID.
+ * @param TResultMap containing result map.
+ */
+ public function __construct($resultMap=null)
+ {
+ if($resultMap instanceof TResultMap)
+ $this->_hostResultMapID = $resultMap->getID();
+ }
+
+ /**
+ * @return mixed null value replacement.
+ */
+ public function getNullValue()
+ {
+ return $this->_nullValue;
+ }
+
+ /**
+ * @param mixed null value replacement.
+ */
+ public function setNullValue($value)
+ {
+ $this->_nullValue = $value;
+ }
+
+ /**
+ * @return string name of a property of the result object that will be set to.
+ */
+ public function getProperty()
+ {
+ return $this->_propertyName;
+ }
+
+ /**
+ * @param string name of a property of the result object that will be set to.
+ */
+ public function setProperty($value)
+ {
+ $this->_propertyName = $value;
+ }
+
+ /**
+ * @return string name of the column in the result set from which the value
+ * will be used to populate the property.
+ */
+ public function getColumn()
+ {
+ return $this->_columnName;
+ }
+
+ /**
+ * @param string name of the column in the result set from which the value
+ * will be used to populate the property.
+ */
+ public function setColumn($value)
+ {
+ $this->_columnName = $value;
+ }
+
+ /**
+ * @return int index of the column in the ResultSet from which the value will
+ * be used to populate the object property
+ */
+ public function getColumnIndex()
+ {
+ return $this->_columnIndex;
+ }
+
+ /**
+ * @param int index of the column in the ResultSet from which the value will
+ * be used to populate the object property
+ */
+ public function setColumnIndex($value)
+ {
+ $this->_columnIndex = TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return string ID of another used to fill the property.
+ */
+ public function getResultMapping()
+ {
+ return $this->_nestedResultMapName;
+ }
+
+ /**
+ * @param string ID of another used to fill the property.
+ */
+ public function setResultMapping($value)
+ {
+ $this->_nestedResultMapName = $value;
+ }
+
+ /**
+ * @return TResultMap nested result map.
+ */
+ public function getNestedResultMap()
+ {
+ return $this->_nestedResultMap;
+ }
+
+ /**
+ * @param TResult nested result map.
+ */
+ public function setNestedResultMap($value)
+ {
+ $this->_nestedResultMap = $value;
+ }
+
+ /**
+ * @return string property type of the object property to be set.
+ */
+ public function getType()
+ {
+ return $this->_valueType;
+ }
+
+ /**
+ * @param string property type of the object property to be set.
+ */
+ public function setType($value)
+ {
+ $this->_valueType = $value;
+ }
+
+ /**
+ * @return string custom type handler class name (may use namespace).
+ */
+ public function getTypeHandler()
+ {
+ return $this->_typeHandler;
+ }
+
+ /**
+ * @param string custom type handler class name (may use namespace).
+ */
+ public function setTypeHandler($value)
+ {
+ $this->_typeHandler = $value;
+ }
+
+ /**
+ * @return string name of another mapped statement
+ */
+ public function getSelect()
+ {
+ return $this->_select;
+ }
+
+ /**
+ * The select property is used to describe a relationship between objects
+ * and to automatically load complex (i.e. user defined) property types.
+ * @param string name of another mapped statement.
+ */
+ public function setSelect($value)
+ {
+ $this->_select = $value;
+ }
+
+ /**
+ * @return boolean indicate whether or not the select statement's results should be lazy loaded
+ */
+ public function getLazyLoad()
+ {
+ return $this->_isLazyLoad;
+ }
+
+ /**
+ * @param boolean indicate whether or not the select statement's results should be lazy loaded
+ */
+ public function setLazyLoad($value)
+ {
+ $this->_isLazyLoad = TPropertyValue::ensureBoolean($value,false);
+ }
+
+ /**
+ * Gets the value for the current property, converts to applicable type if necessary.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array result row
+ * @return mixed property value.
+ */
+ public function getPropertyValue($registry,$row)
+ {
+ $value = null;
+ $index = $this->getColumnIndex();
+ $name = $this->getColumn();
+ if($index > 0 && isset($row[$index]))
+ $value = $this->getTypedValue($registry,$row[$index]);
+ else if(isset($row[$name]))
+ $value = $this->getTypedValue($registry,$row[$name]);
+ if(($value===null) && ($this->getNullValue()!==null))
+ $value = $this->getTypedValue($registry,$this->getNullValue());
+ return $value;
+ }
+
+ /**
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param mixed raw property value
+ * @return mixed property value casted to specific type.
+ */
+ protected function getTypedValue($registry,$value)
+ {
+ if(($handler = $this->createTypeHandler($registry))!==null)
+ return $handler->getResult($value);
+ else
+ return $registry->convertToType($this->getType(), $value);
+ }
+
+ /**
+ * Create type handler from {@link Type setType()} or {@link TypeHandler setTypeHandler}.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @return TSqlMapTypeHandler type handler.
+ */
+ protected function createTypeHandler($registry)
+ {
+ $type=$this->getTypeHandler() ? $this->getTypeHandler() : $this->getType();
+ $handler=$registry->getTypeHandler($type);
+ if($handler===null && $this->getTypeHandler())
+ $handler = Prado::createComponent($type);
+ return $handler;
+ }
+
+ /**
+ * Determines if the type is an instance of ArrayAccess, TList or an array.
+ * @return int TResultProperty::LIST_TYPE or TResultProperty::ARRAY_TYPE
+ */
+ protected function getPropertyValueType()
+ {
+ if(class_exists($type = $this->getType(), false)) //NO force autoloading
+ {
+ if($type==='TList')
+ return self::LIST_TYPE;
+ $class = new ReflectionClass($type);
+ if($class->isSubclassOf('TList'))
+ return self::LIST_TYPE;
+ if($class->implementsInterface('ArrayAccess'))
+ return self::ARRAY_TYPE;
+ }
+ if(strtolower($type) == 'array')
+ return self::ARRAY_TYPE;
+ }
+
+ /**
+ * Returns true if the result property {@link Type getType()} is of TList type
+ * or that the actual result object is an instance of TList.
+ * @param object result object
+ * @return boolean true if the result object is an instance of TList
+ */
+ public function instanceOfListType($target)
+ {
+ if($this->getType()===null)
+ return TPropertyAccess::get($target,$this->getProperty()) instanceof TList;
+ return $this->getPropertyValueType() == self::LIST_TYPE;
+ }
+
+ /**
+ * Returns true if the result property {@link Type getType()} is of ArrayAccess
+ * or that the actual result object is an array or implements ArrayAccess
+ * @param object result object
+ * @return boolean true if the result object is an instance of ArrayAccess or is an array.
+ */
+ public function instanceOfArrayType($target)
+ {
+ if($this->getType()===null)
+ {
+ $prop = TPropertyAccess::get($target,$this->getProperty());
+ if(is_object($prop))
+ return $prop instanceof ArrayAccess;
+ return is_array($prop);
+ }
+ return $this->getPropertyValueType() == self::ARRAY_TYPE;
+ }
+
+ public function __sleep()
+ {
+ $exprops = array(); $cn = 'TResultProperty';
+ if ($this->_nullValue===null) $exprops[] = "\0$cn\0_nullValue";
+ if ($this->_propertyName===null) $exprops[] = "\0$cn\0_propertyNama";
+ if ($this->_columnName===null) $exprops[] = "\0$cn\0_columnName";
+ if ($this->_columnIndex==-1) $exprops[] = "\0$cn\0_columnIndex";
+ if ($this->_nestedResultMapName===null) $exprops[] = "\0$cn\0_nestedResultMapName";
+ if ($this->_nestedResultMap===null) $exprops[] = "\0$cn\0_nestedResultMap";
+ if ($this->_valueType===null) $exprops[] = "\0$cn\0_valueType";
+ if ($this->_typeHandler===null) $exprops[] = "\0$cn\0_typeHandler";
+ if ($this->_isLazyLoad===false) $exprops[] = "\0$cn\0_isLazyLoad";
+ if ($this->_select===null) $exprops[] = "\0$cn\0_select";
+ return array_diff(parent::__sleep(),$exprops);
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php
index 1ceba9eb..a70e6b3f 100644
--- a/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php
+++ b/framework/Data/SqlMap/Configuration/TSimpleDynamicParser.php
@@ -1,45 +1,45 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces
- * it with a TSimpleDynamicParser::DYNAMIC_TOKEN.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSimpleDynamicParser
-{
- const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/';
- const DYNAMIC_TOKEN = '`!`';
-
- /**
- * Parse the sql text for dynamic place holders of the form $name$.
- * @param string Sql text.
- * @return array name value pairs 'sql' and 'parameters'.
- */
- public function parse($sqlText)
- {
- $matches = array();
- $mappings = array();
- preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
- for($i = 0, $k=count($matches[1]); $i<$k; $i++)
- {
- $mappings[] = $matches[1][$i];
- $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText);
- }
- return array('sql'=>$sqlText, 'parameters'=>$mappings);
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TSimpleDynamicParser finds place holders $name$ in the sql text and replaces
+ * it with a TSimpleDynamicParser::DYNAMIC_TOKEN.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSimpleDynamicParser
+{
+ const PARAMETER_TOKEN_REGEXP = '/\$([^\$]+)\$/';
+ const DYNAMIC_TOKEN = '`!`';
+
+ /**
+ * Parse the sql text for dynamic place holders of the form $name$.
+ * @param string Sql text.
+ * @return array name value pairs 'sql' and 'parameters'.
+ */
+ public function parse($sqlText)
+ {
+ $matches = array();
+ $mappings = array();
+ preg_match_all(self::PARAMETER_TOKEN_REGEXP, $sqlText, $matches);
+ for($i = 0, $k=count($matches[1]); $i<$k; $i++)
+ {
+ $mappings[] = $matches[1][$i];
+ $sqlText = str_replace($matches[0][$i], self::DYNAMIC_TOKEN, $sqlText);
+ }
+ return array('sql'=>$sqlText, 'parameters'=>$mappings);
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
index 4b985e51..0dc5d821 100644
--- a/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
+++ b/framework/Data/SqlMap/Configuration/TSqlMapCacheModel.php
@@ -1,246 +1,246 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSqlMapCacheModel corresponds to the sql mapping configuration tag.
- *
- * The results from a query Mapped Statement can be cached simply by specifying
- * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in tag.
- * A cache model is a configured cache that is defined within the sql map
- * configuration file. Cache models are configured using the element.
- *
- * The cache model uses a pluggable framework for supporting different types of
- * caches. The choice of cache is specified by the {@link Implementation setImplementation()}
- * property. The class name specified must be one of {@link TSqlMapCacheTypes}.
- *
- * The cache implementations, LRU and FIFO cache below do not persist across
- * requests. That is, once the request is complete, all cache data is lost.
- * These caches are useful queries that results in the same repeated data during
- * the current request.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheModel extends TComponent
-{
- private $_cache;
- private $_hits = 0;
- private $_requests = 0;
- private $_id;
- private $_implementation=TSqlMapCacheTypes::Basic;
- private $_properties = array();
- private $_flushInterval = 0;
-
- private static $_cacheTypes = array();
-
- public static function registerCacheType($type, $className)
- {
- self::$_cacheTypes[$type] = $className;
- }
-
- /**
- * @return string unique cache model identifier.
- */
- public function getID()
- {
- return $this->_id;
- }
-
- /**
- * @param string unique cache model identifier.
- */
- public function setID($value)
- {
- $this->_id = $value;
- }
-
- /**
- * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
- */
- public function getImplementation()
- {
- return $this->_implementation;
- }
-
- /**
- * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
- */
- public function setImplementation($value)
- {
- if (isset(self::$_cacheTypes[$value]))
- $this->_implementation = $value;
- else
- $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes');
- }
-
- /**
- * @param integer the number of seconds in which the cached value will expire. 0 means never expire.
- */
- public function setFlushInterval($value)
- {
- $this->_flushInterval=TPropertyValue::ensureInteger($value);
- }
-
- /**
- * @return integer cache duration.
- */
- public function getFlushInterval()
- {
- return $this->_flushInterval;
- }
-
- /**
- * Initialize the cache implementation, sets the actual cache contain if supplied.
- * @param ISqLMapCache cache implementation instance.
- */
- public function initialize($cache=null)
- {
- if($cache===null)
- $this->_cache= Prado::createComponent($this->getImplementationClass(), $this);
- else
- $this->_cache=$cache;
- }
-
- /**
- * @return string cache implementation class name.
- */
- public function getImplementationClass()
- {
- $implementation = $this->_implementation;
- if (isset(self::$_cacheTypes[$implementation])) return self::$_cacheTypes[$implementation];
-
- switch(TPropertyValue::ensureEnum($implementation,'TSqlMapCacheTypes'))
- {
- case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache';
- case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache';
- case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache';
- }
- }
-
- /**
- * Register a mapped statement that will trigger a cache flush.
- * @param TMappedStatement mapped statement that may flush the cache.
- */
- public function registerTriggerStatement($mappedStatement)
- {
- $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush'));
- }
-
- /**
- * Clears the cache.
- */
- public function flush()
- {
- $this->_cache->flush();
- }
-
- /**
- * @param TSqlMapCacheKey|string cache key
- * @return mixed cached value.
- */
- public function get($key)
- {
- if($key instanceof TSqlMapCacheKey)
- $key = $key->getHash();
-
- //if flush ?
- $value = $this->_cache->get($key);
- $this->_requests++;
- if($value!==null)
- $this->_hits++;
- return $value;
- }
-
- /**
- * @param TSqlMapCacheKey|string cache key
- * @param mixed value to be cached.
- */
- public function set($key, $value)
- {
- if($key instanceof TSqlMapCacheKey)
- $key = $key->getHash();
-
- if($value!==null)
- $this->_cache->set($key, $value, $this->_flushInterval);
- }
-
- /**
- * @return float cache hit ratio.
- */
- public function getHitRatio()
- {
- if($this->_requests != 0)
- return $this->_hits / $this->_requests;
- else
- return 0;
- }
-}
-
-/**
- * TSqlMapCacheTypes enumerable class.
- *
- * Implemented cache are 'Basic', 'FIFO' and 'LRU'.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheTypes extends TEnumerable
-{
- const Basic='Basic';
- const FIFO='FIFO';
- const LRU='LRU';
-}
-
-/**
- * TSqlMapCacheKey class.
- *
- * Provides a hash of the object to be cached.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapCacheKey
-{
- private $_key;
-
- /**
- * @param mixed object to be cached.
- */
- public function __construct($object)
- {
- $this->_key = $this->generateKey(serialize($object));
- }
-
- /**
- * @param string serialized object
- * @return string crc32 hash of the serialized object.
- */
- protected function generateKey($string)
- {
- return sprintf('%x',crc32($string));
- }
-
- /**
- * @return string object hash.
- */
- public function getHash()
- {
- return $this->_key;
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TSqlMapCacheModel corresponds to the sql mapping configuration tag.
+ *
+ * The results from a query Mapped Statement can be cached simply by specifying
+ * the {@link CacheModel TSqlMapStatement::setCacheModel()} property in tag.
+ * A cache model is a configured cache that is defined within the sql map
+ * configuration file. Cache models are configured using the element.
+ *
+ * The cache model uses a pluggable framework for supporting different types of
+ * caches. The choice of cache is specified by the {@link Implementation setImplementation()}
+ * property. The class name specified must be one of {@link TSqlMapCacheTypes}.
+ *
+ * The cache implementations, LRU and FIFO cache below do not persist across
+ * requests. That is, once the request is complete, all cache data is lost.
+ * These caches are useful queries that results in the same repeated data during
+ * the current request.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheModel extends TComponent
+{
+ private $_cache;
+ private $_hits = 0;
+ private $_requests = 0;
+ private $_id;
+ private $_implementation=TSqlMapCacheTypes::Basic;
+ private $_properties = array();
+ private $_flushInterval = 0;
+
+ private static $_cacheTypes = array();
+
+ public static function registerCacheType($type, $className)
+ {
+ self::$_cacheTypes[$type] = $className;
+ }
+
+ /**
+ * @return string unique cache model identifier.
+ */
+ public function getID()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * @param string unique cache model identifier.
+ */
+ public function setID($value)
+ {
+ $this->_id = $value;
+ }
+
+ /**
+ * @return string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
+ */
+ public function getImplementation()
+ {
+ return $this->_implementation;
+ }
+
+ /**
+ * @param string cache implements of TSqlMapCacheTypes, either 'Basic', 'LRU' or 'FIFO'.
+ */
+ public function setImplementation($value)
+ {
+ if (isset(self::$_cacheTypes[$value]))
+ $this->_implementation = $value;
+ else
+ $this->_implementation = TPropertyValue::ensureEnum($value,'TSqlMapCacheTypes');
+ }
+
+ /**
+ * @param integer the number of seconds in which the cached value will expire. 0 means never expire.
+ */
+ public function setFlushInterval($value)
+ {
+ $this->_flushInterval=TPropertyValue::ensureInteger($value);
+ }
+
+ /**
+ * @return integer cache duration.
+ */
+ public function getFlushInterval()
+ {
+ return $this->_flushInterval;
+ }
+
+ /**
+ * Initialize the cache implementation, sets the actual cache contain if supplied.
+ * @param ISqLMapCache cache implementation instance.
+ */
+ public function initialize($cache=null)
+ {
+ if($cache===null)
+ $this->_cache= Prado::createComponent($this->getImplementationClass(), $this);
+ else
+ $this->_cache=$cache;
+ }
+
+ /**
+ * @return string cache implementation class name.
+ */
+ public function getImplementationClass()
+ {
+ $implementation = $this->_implementation;
+ if (isset(self::$_cacheTypes[$implementation])) return self::$_cacheTypes[$implementation];
+
+ switch(TPropertyValue::ensureEnum($implementation,'TSqlMapCacheTypes'))
+ {
+ case TSqlMapCacheTypes::FIFO: return 'TSqlMapFifoCache';
+ case TSqlMapCacheTypes::LRU : return 'TSqlMapLruCache';
+ case TSqlMapCacheTypes::Basic : return 'TSqlMapApplicationCache';
+ }
+ }
+
+ /**
+ * Register a mapped statement that will trigger a cache flush.
+ * @param TMappedStatement mapped statement that may flush the cache.
+ */
+ public function registerTriggerStatement($mappedStatement)
+ {
+ $mappedStatement->attachEventHandler('OnExecuteQuery',array($this, 'flush'));
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public function flush()
+ {
+ $this->_cache->flush();
+ }
+
+ /**
+ * @param TSqlMapCacheKey|string cache key
+ * @return mixed cached value.
+ */
+ public function get($key)
+ {
+ if($key instanceof TSqlMapCacheKey)
+ $key = $key->getHash();
+
+ //if flush ?
+ $value = $this->_cache->get($key);
+ $this->_requests++;
+ if($value!==null)
+ $this->_hits++;
+ return $value;
+ }
+
+ /**
+ * @param TSqlMapCacheKey|string cache key
+ * @param mixed value to be cached.
+ */
+ public function set($key, $value)
+ {
+ if($key instanceof TSqlMapCacheKey)
+ $key = $key->getHash();
+
+ if($value!==null)
+ $this->_cache->set($key, $value, $this->_flushInterval);
+ }
+
+ /**
+ * @return float cache hit ratio.
+ */
+ public function getHitRatio()
+ {
+ if($this->_requests != 0)
+ return $this->_hits / $this->_requests;
+ else
+ return 0;
+ }
+}
+
+/**
+ * TSqlMapCacheTypes enumerable class.
+ *
+ * Implemented cache are 'Basic', 'FIFO' and 'LRU'.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheTypes extends TEnumerable
+{
+ const Basic='Basic';
+ const FIFO='FIFO';
+ const LRU='LRU';
+}
+
+/**
+ * TSqlMapCacheKey class.
+ *
+ * Provides a hash of the object to be cached.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapCacheKey
+{
+ private $_key;
+
+ /**
+ * @param mixed object to be cached.
+ */
+ public function __construct($object)
+ {
+ $this->_key = $this->generateKey(serialize($object));
+ }
+
+ /**
+ * @param string serialized object
+ * @return string crc32 hash of the serialized object.
+ */
+ protected function generateKey($string)
+ {
+ return sprintf('%x',crc32($string));
+ }
+
+ /**
+ * @return string object hash.
+ */
+ public function getHash()
+ {
+ return $this->_key;
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php
index 1d90d4b1..8f2ca5cc 100644
--- a/framework/Data/SqlMap/Configuration/TSqlMapStatement.php
+++ b/framework/Data/SqlMap/Configuration/TSqlMapStatement.php
@@ -1,451 +1,451 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-/**
- * TSqlMapStatement class corresponds to element.
- *
- * Mapped Statements can hold any SQL statement and can use Parameter Maps
- * and Result Maps for input and output.
- *
- * The element is a general "catch all" element that can be used
- * for any type of SQL statement. Generally it is a good idea to use one of the
- * more specific statement-type elements. The more specific elements provided
- * better error-checking and even more functionality. (For example, the insert
- * statement can return a database-generated key.)
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapStatement extends TComponent
-{
- private $_parameterMapName;
- private $_parameterMap;
- private $_parameterClassName;
- private $_resultMapName;
- private $_resultMap;
- private $_resultClassName;
- private $_cacheModelName;
- private $_SQL;
- private $_listClass;
- private $_typeHandler;
- private $_extendStatement;
- private $_cache;
- private $_ID;
-
- /**
- * @return string name for this statement, unique to each sql map manager.
- */
- public function getID()
- {
- return $this->_ID;
- }
-
- /**
- * @param string name for this statement, which must be unique for each sql map manager.
- */
- public function setID($value)
- {
- $this->_ID=$value;
- }
-
- /**
- * @return string name of a parameter map.
- */
- public function getParameterMap()
- {
- return $this->_parameterMapName;
- }
-
- /**
- * A Parameter Map defines an ordered list of values that match up with
- * the "?" placeholders of a standard, parameterized query statement.
- * @param string parameter map name.
- */
- public function setParameterMap($value)
- {
- $this->_parameterMapName = $value;
- }
-
- /**
- * @return string parameter class name.
- */
- public function getParameterClass()
- {
- return $this->_parameterClassName;
- }
-
- /**
- * If a {@link ParameterMap setParameterMap()} property is not specified,
- * you may specify a ParameterClass instead and use inline parameters.
- * The value of the parameterClass attribute can be any existing PHP class name.
- * @param string parameter class name.
- */
- public function setParameterClass($value)
- {
- $this->_parameterClassName = $value;
- }
-
- /**
- * @return string result map name.
- */
- public function getResultMap()
- {
- return $this->_resultMapName;
- }
-
- /**
- * A Result Map lets you control how data is extracted from the result of a
- * query, and how the columns are mapped to object properties.
- * @param string result map name.
- */
- public function setResultMap($value)
- {
- $this->_resultMapName = $value;
- }
-
- /**
- * @return string result class name.
- */
- public function getResultClass()
- {
- return $this->_resultClassName;
- }
-
- /**
- * If a {@link ResultMap setResultMap()} is not specified, you may specify a
- * ResultClass instead. The value of the ResultClass property can be the
- * name of a PHP class or primitives like integer, string, or array. The
- * class specified will be automatically mapped to the columns in the
- * result, based on the result metadata.
- * @param string result class name.
- */
- public function setResultClass($value)
- {
- $this->_resultClassName = $value;
- }
-
- /**
- * @return string cache mode name.
- */
- public function getCacheModel()
- {
- return $this->_cacheModelName;
- }
-
- /**
- * @param string cache mode name.
- */
- public function setCacheModel($value)
- {
- $this->_cacheModelName = $value;
- }
-
- /**
- * @return TSqlMapCacheModel cache implementation instance for this statement.
- */
- public function getCache()
- {
- return $this->_cache;
- }
-
- /**
- * @param TSqlMapCacheModel cache implementation instance for this statement.
- */
- public function setCache($value)
- {
- $this->_cache = $value;
- }
-
- /**
- * @return TStaticSql sql text container.
- */
- public function getSqlText()
- {
- return $this->_SQL;
- }
-
- /**
- * @param TStaticSql sql text container.
- */
- public function setSqlText($value)
- {
- $this->_SQL = $value;
- }
-
- /**
- * @return string name of a PHP class that implements ArrayAccess.
- */
- public function getListClass()
- {
- return $this->_listClass;
- }
-
- /**
- * An ArrayAccess class can be specified to handle the type of objects in the collection.
- * @param string name of a PHP class that implements ArrayAccess.
- */
- public function setListClass($value)
- {
- $this->_listClass = $value;
- }
-
- /**
- * @return string another statement element name.
- */
- public function getExtends()
- {
- return $this->_extendStatement;
- }
-
- /**
- * @param string name of another statement element to extend.
- */
- public function setExtends($value)
- {
- $this->_extendStatement = $value;
- }
-
- /**
- * @return TResultMap the result map corresponding to the
- * {@link ResultMap getResultMap()} property.
- */
- public function resultMap()
- {
- return $this->_resultMap;
- }
-
- /**
- * @return TParameterMap the parameter map corresponding to the
- * {@link ParameterMap getParameterMap()} property.
- */
- public function parameterMap()
- {
- return $this->_parameterMap;
- }
-
- /**
- * @param TInlineParameterMap parameter extracted from the sql text.
- */
- public function setInlineParameterMap($map)
- {
- $this->_parameterMap = $map;
- }
-
- /**
- * @param TSqlMapManager initialize the statement, sets the result and parameter maps.
- */
- public function initialize($manager)
- {
- if(strlen($this->_resultMapName) > 0)
- $this->_resultMap = $manager->getResultMap($this->_resultMapName);
- if(strlen($this->_parameterMapName) > 0)
- $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName);
- }
-
- /**
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @return ArrayAccess new instance of list class.
- */
- public function createInstanceOfListClass($registry)
- {
- if(strlen($type = $this->getListClass()) > 0)
- return $this->createInstanceOf($registry,$type);
- return array();
- }
-
- /**
- * Create a new instance of a given type.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param string result class name.
- * @param array result data.
- * @return mixed result object.
- */
- protected function createInstanceOf($registry,$type,$row=null)
- {
- $handler = $registry->getTypeHandler($type);
- if($handler!==null)
- return $handler->createNewInstance($row);
- else
- return $registry->createInstanceOf($type);
- }
-
- /**
- * Create a new instance of result class.
- * @param TSqlMapTypeHandlerRegistry type handler registry
- * @param array result data.
- * @return mixed result object.
- */
- public function createInstanceOfResultClass($registry,$row)
- {
- if(strlen($type= $this->getResultClass()) > 0)
- return $this->createInstanceOf($registry,$type,$row);
- }
-
- public function __sleep()
- {
- $cn = __CLASS__;
- $exprops = array("\0$cn\0_resultMap");
- if (!$this->_parameterMapName) $exprops[] = "\0$cn\0_parameterMapName";
- if (!$this->_parameterMap) $exprops[] = "\0$cn\0_parameterMap";
- if (!$this->_parameterClassName) $exprops[] = "\0$cn\0_parameterClassName";
- if (!$this->_resultMapName) $exprops[] = "\0$cn\0_resultMapName";
- if (!$this->_resultMap) $exprops[] = "\0$cn\0_resultMap";
- if (!$this->_resultClassName) $exprops[] = "\0$cn\0_resultClassName";
- if (!$this->_cacheModelName) $exprops[] = "\0$cn\0_cacheModelName";
- if (!$this->_SQL) $exprops[] = "\0$cn\0_SQL";
- if (!$this->_listClass) $exprops[] = "\0$cn\0_listClass";
- if (!$this->_typeHandler) $exprops[] = "\0$cn\0_typeHandler";
- if (!$this->_extendStatement) $exprops[] = "\0$cn\0_extendStatement";
- if (!$this->_cache) $exprops[] = "\0$cn\0_cache";
-
- return array_diff(parent::__sleep(),$exprops);
- }
-
-}
-
-/**
- * TSqlMapSelect class file.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Statements
- * @since 3.1
- */
-class TSqlMapSelect extends TSqlMapStatement
-{
- private $_generate;
-
- public function getGenerate(){ return $this->_generate; }
- public function setGenerate($value){ $this->_generate = $value; }
-}
-
-/**
- * TSqlMapInsert class corresponds to the element.
- *
- * The element allows child elements that can be used
- * to generate a key to be used for the insert command.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapInsert extends TSqlMapStatement
-{
- private $_selectKey=null;
-
- /**
- * @return TSqlMapSelectKey select key element.
- */
- public function getSelectKey()
- {
- return $this->_selectKey;
- }
-
- /**
- * @param TSqlMapSelectKey select key.
- */
- public function setSelectKey($value)
- {
- $this->_selectKey = $value;
- }
-}
-
-/**
- * TSqlMapUpdate class corresponds to element.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapUpdate extends TSqlMapStatement
-{
-}
-
-/**
- * TSqlMapDelete class corresponds to the element.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapDelete extends TSqlMapUpdate
-{
-}
-
-/**
- * TSqlMapSelect corresponds to the element.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapSelectKey extends TSqlMapStatement
-{
- private $_type = 'post';
- private $_property;
-
- /**
- * @return string select generated key type, 'post' or 'pre'.
- */
- public function getType()
- {
- return $this->_type;
- }
-
- /**
- * @param string select generated key type, 'post' or 'pre'.
- */
- public function setType($value)
- {
- $this->_type = strtolower($value) == 'post' ? 'post' : 'pre';
- }
-
- /**
- * @return string property name for the generated key.
- */
- public function getProperty()
- {
- return $this->_property;
- }
-
- /**
- * @param string property name for the generated key.
- */
- public function setProperty($value)
- {
- $this->_property = $value;
- }
-
- /**
- * @throws TSqlMapConfigurationException extends is unsupported.
- */
- public function setExtends($value)
- {
- throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key');
- }
-
- /**
- * @return boolean true if key is generated after insert command, false otherwise.
- */
- public function getIsAfter()
- {
- return $this->_type == 'post';
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+/**
+ * TSqlMapStatement class corresponds to element.
+ *
+ * Mapped Statements can hold any SQL statement and can use Parameter Maps
+ * and Result Maps for input and output.
+ *
+ * The element is a general "catch all" element that can be used
+ * for any type of SQL statement. Generally it is a good idea to use one of the
+ * more specific statement-type elements. The more specific elements provided
+ * better error-checking and even more functionality. (For example, the insert
+ * statement can return a database-generated key.)
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapStatement extends TComponent
+{
+ private $_parameterMapName;
+ private $_parameterMap;
+ private $_parameterClassName;
+ private $_resultMapName;
+ private $_resultMap;
+ private $_resultClassName;
+ private $_cacheModelName;
+ private $_SQL;
+ private $_listClass;
+ private $_typeHandler;
+ private $_extendStatement;
+ private $_cache;
+ private $_ID;
+
+ /**
+ * @return string name for this statement, unique to each sql map manager.
+ */
+ public function getID()
+ {
+ return $this->_ID;
+ }
+
+ /**
+ * @param string name for this statement, which must be unique for each sql map manager.
+ */
+ public function setID($value)
+ {
+ $this->_ID=$value;
+ }
+
+ /**
+ * @return string name of a parameter map.
+ */
+ public function getParameterMap()
+ {
+ return $this->_parameterMapName;
+ }
+
+ /**
+ * A Parameter Map defines an ordered list of values that match up with
+ * the "?" placeholders of a standard, parameterized query statement.
+ * @param string parameter map name.
+ */
+ public function setParameterMap($value)
+ {
+ $this->_parameterMapName = $value;
+ }
+
+ /**
+ * @return string parameter class name.
+ */
+ public function getParameterClass()
+ {
+ return $this->_parameterClassName;
+ }
+
+ /**
+ * If a {@link ParameterMap setParameterMap()} property is not specified,
+ * you may specify a ParameterClass instead and use inline parameters.
+ * The value of the parameterClass attribute can be any existing PHP class name.
+ * @param string parameter class name.
+ */
+ public function setParameterClass($value)
+ {
+ $this->_parameterClassName = $value;
+ }
+
+ /**
+ * @return string result map name.
+ */
+ public function getResultMap()
+ {
+ return $this->_resultMapName;
+ }
+
+ /**
+ * A Result Map lets you control how data is extracted from the result of a
+ * query, and how the columns are mapped to object properties.
+ * @param string result map name.
+ */
+ public function setResultMap($value)
+ {
+ $this->_resultMapName = $value;
+ }
+
+ /**
+ * @return string result class name.
+ */
+ public function getResultClass()
+ {
+ return $this->_resultClassName;
+ }
+
+ /**
+ * If a {@link ResultMap setResultMap()} is not specified, you may specify a
+ * ResultClass instead. The value of the ResultClass property can be the
+ * name of a PHP class or primitives like integer, string, or array. The
+ * class specified will be automatically mapped to the columns in the
+ * result, based on the result metadata.
+ * @param string result class name.
+ */
+ public function setResultClass($value)
+ {
+ $this->_resultClassName = $value;
+ }
+
+ /**
+ * @return string cache mode name.
+ */
+ public function getCacheModel()
+ {
+ return $this->_cacheModelName;
+ }
+
+ /**
+ * @param string cache mode name.
+ */
+ public function setCacheModel($value)
+ {
+ $this->_cacheModelName = $value;
+ }
+
+ /**
+ * @return TSqlMapCacheModel cache implementation instance for this statement.
+ */
+ public function getCache()
+ {
+ return $this->_cache;
+ }
+
+ /**
+ * @param TSqlMapCacheModel cache implementation instance for this statement.
+ */
+ public function setCache($value)
+ {
+ $this->_cache = $value;
+ }
+
+ /**
+ * @return TStaticSql sql text container.
+ */
+ public function getSqlText()
+ {
+ return $this->_SQL;
+ }
+
+ /**
+ * @param TStaticSql sql text container.
+ */
+ public function setSqlText($value)
+ {
+ $this->_SQL = $value;
+ }
+
+ /**
+ * @return string name of a PHP class that implements ArrayAccess.
+ */
+ public function getListClass()
+ {
+ return $this->_listClass;
+ }
+
+ /**
+ * An ArrayAccess class can be specified to handle the type of objects in the collection.
+ * @param string name of a PHP class that implements ArrayAccess.
+ */
+ public function setListClass($value)
+ {
+ $this->_listClass = $value;
+ }
+
+ /**
+ * @return string another statement element name.
+ */
+ public function getExtends()
+ {
+ return $this->_extendStatement;
+ }
+
+ /**
+ * @param string name of another statement element to extend.
+ */
+ public function setExtends($value)
+ {
+ $this->_extendStatement = $value;
+ }
+
+ /**
+ * @return TResultMap the result map corresponding to the
+ * {@link ResultMap getResultMap()} property.
+ */
+ public function resultMap()
+ {
+ return $this->_resultMap;
+ }
+
+ /**
+ * @return TParameterMap the parameter map corresponding to the
+ * {@link ParameterMap getParameterMap()} property.
+ */
+ public function parameterMap()
+ {
+ return $this->_parameterMap;
+ }
+
+ /**
+ * @param TInlineParameterMap parameter extracted from the sql text.
+ */
+ public function setInlineParameterMap($map)
+ {
+ $this->_parameterMap = $map;
+ }
+
+ /**
+ * @param TSqlMapManager initialize the statement, sets the result and parameter maps.
+ */
+ public function initialize($manager)
+ {
+ if(strlen($this->_resultMapName) > 0)
+ $this->_resultMap = $manager->getResultMap($this->_resultMapName);
+ if(strlen($this->_parameterMapName) > 0)
+ $this->_parameterMap = $manager->getParameterMap($this->_parameterMapName);
+ }
+
+ /**
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @return ArrayAccess new instance of list class.
+ */
+ public function createInstanceOfListClass($registry)
+ {
+ if(strlen($type = $this->getListClass()) > 0)
+ return $this->createInstanceOf($registry,$type);
+ return array();
+ }
+
+ /**
+ * Create a new instance of a given type.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param string result class name.
+ * @param array result data.
+ * @return mixed result object.
+ */
+ protected function createInstanceOf($registry,$type,$row=null)
+ {
+ $handler = $registry->getTypeHandler($type);
+ if($handler!==null)
+ return $handler->createNewInstance($row);
+ else
+ return $registry->createInstanceOf($type);
+ }
+
+ /**
+ * Create a new instance of result class.
+ * @param TSqlMapTypeHandlerRegistry type handler registry
+ * @param array result data.
+ * @return mixed result object.
+ */
+ public function createInstanceOfResultClass($registry,$row)
+ {
+ if(strlen($type= $this->getResultClass()) > 0)
+ return $this->createInstanceOf($registry,$type,$row);
+ }
+
+ public function __sleep()
+ {
+ $cn = __CLASS__;
+ $exprops = array("\0$cn\0_resultMap");
+ if (!$this->_parameterMapName) $exprops[] = "\0$cn\0_parameterMapName";
+ if (!$this->_parameterMap) $exprops[] = "\0$cn\0_parameterMap";
+ if (!$this->_parameterClassName) $exprops[] = "\0$cn\0_parameterClassName";
+ if (!$this->_resultMapName) $exprops[] = "\0$cn\0_resultMapName";
+ if (!$this->_resultMap) $exprops[] = "\0$cn\0_resultMap";
+ if (!$this->_resultClassName) $exprops[] = "\0$cn\0_resultClassName";
+ if (!$this->_cacheModelName) $exprops[] = "\0$cn\0_cacheModelName";
+ if (!$this->_SQL) $exprops[] = "\0$cn\0_SQL";
+ if (!$this->_listClass) $exprops[] = "\0$cn\0_listClass";
+ if (!$this->_typeHandler) $exprops[] = "\0$cn\0_typeHandler";
+ if (!$this->_extendStatement) $exprops[] = "\0$cn\0_extendStatement";
+ if (!$this->_cache) $exprops[] = "\0$cn\0_cache";
+
+ return array_diff(parent::__sleep(),$exprops);
+ }
+
+}
+
+/**
+ * TSqlMapSelect class file.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Statements
+ * @since 3.1
+ */
+class TSqlMapSelect extends TSqlMapStatement
+{
+ private $_generate;
+
+ public function getGenerate(){ return $this->_generate; }
+ public function setGenerate($value){ $this->_generate = $value; }
+}
+
+/**
+ * TSqlMapInsert class corresponds to the element.
+ *
+ * The element allows child elements that can be used
+ * to generate a key to be used for the insert command.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapInsert extends TSqlMapStatement
+{
+ private $_selectKey=null;
+
+ /**
+ * @return TSqlMapSelectKey select key element.
+ */
+ public function getSelectKey()
+ {
+ return $this->_selectKey;
+ }
+
+ /**
+ * @param TSqlMapSelectKey select key.
+ */
+ public function setSelectKey($value)
+ {
+ $this->_selectKey = $value;
+ }
+}
+
+/**
+ * TSqlMapUpdate class corresponds to element.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapUpdate extends TSqlMapStatement
+{
+}
+
+/**
+ * TSqlMapDelete class corresponds to the element.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapDelete extends TSqlMapUpdate
+{
+}
+
+/**
+ * TSqlMapSelect corresponds to the element.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapSelectKey extends TSqlMapStatement
+{
+ private $_type = 'post';
+ private $_property;
+
+ /**
+ * @return string select generated key type, 'post' or 'pre'.
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * @param string select generated key type, 'post' or 'pre'.
+ */
+ public function setType($value)
+ {
+ $this->_type = strtolower($value) == 'post' ? 'post' : 'pre';
+ }
+
+ /**
+ * @return string property name for the generated key.
+ */
+ public function getProperty()
+ {
+ return $this->_property;
+ }
+
+ /**
+ * @param string property name for the generated key.
+ */
+ public function setProperty($value)
+ {
+ $this->_property = $value;
+ }
+
+ /**
+ * @throws TSqlMapConfigurationException extends is unsupported.
+ */
+ public function setExtends($value)
+ {
+ throw new TSqlMapConfigurationException('sqlmap_can_not_extend_select_key');
+ }
+
+ /**
+ * @return boolean true if key is generated after insert command, false otherwise.
+ */
+ public function getIsAfter()
+ {
+ return $this->_type == 'post';
+ }
+}
+
diff --git a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
index a60827fe..988d00db 100644
--- a/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
+++ b/framework/Data/SqlMap/Configuration/TSqlMapXmlConfiguration.php
@@ -1,805 +1,805 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-
-Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
-
-/**
- * TSqlMapXmlConfig class file.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- */
-abstract class TSqlMapXmlConfigBuilder
-{
- /**
- * Create an instance of an object give by the attribute named 'class' in the
- * node and set the properties on the object given by attribute names and values.
- * @param SimpleXmlNode property node
- * @return Object new instance of class with class name given by 'class' attribute value.
- */
- protected function createObjectFromNode($node)
- {
- if(isset($node['class']))
- {
- $obj = Prado::createComponent((string)$node['class']);
- $this->setObjectPropFromNode($obj,$node,array('class'));
- return $obj;
- }
- throw new TSqlMapConfigurationException(
- 'sqlmap_node_class_undef', $node, $this->getConfigFile());
- }
-
- /**
- * For each attributes (excluding attribute named in $except) set the
- * property of the $obj given by the name of the attribute with the value
- * of the attribute.
- * @param Object object instance
- * @param SimpleXmlNode property node
- * @param array exception property name
- */
- protected function setObjectPropFromNode($obj,$node,$except=array())
- {
- foreach($node->attributes() as $name=>$value)
- {
- if(!in_array($name,$except))
- {
- if($obj->canSetProperty($name))
- $obj->{$name} = (string)$value;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_invalid_property', $name, get_class($obj),
- $node, $this->getConfigFile());
- }
- }
- }
-
- /**
- * Gets the filename relative to the basefile.
- * @param string base filename
- * @param string relative filename
- * @return string absolute filename.
- */
- protected function getAbsoluteFilePath($basefile,$resource)
- {
- $basedir = dirname($basefile);
- $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource);
- if(!is_string($file) || !is_file($file))
- $file = realpath($resource);
- if(is_string($file) && is_file($file))
- return $file;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_resource', $resource);
- }
-
- /**
- * Load document using simple xml.
- * @param string filename.
- * @return SimpleXmlElement xml document.
- */
- protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config)
- {
- if( strpos($filename, '${') !== false)
- $filename = $config->replaceProperties($filename);
-
- if(!is_file($filename))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_config', $filename);
- return simplexml_load_string($config->replaceProperties(file_get_contents($filename)));
- }
-
- /**
- * Get element node by ID value (try for attribute name ID as case insensitive).
- * @param SimpleXmlDocument $document
- * @param string tag name.
- * @param string id value.
- * @return SimpleXmlElement node if found, null otherwise.
- */
- protected function getElementByIdValue($document, $tag, $value)
- {
- //hack to allow upper case and lower case attribute names.
- foreach(array('id','ID','Id', 'iD') as $id)
- {
- $xpath = "//{$tag}[@{$id}='{$value}']";
- foreach($document->xpath($xpath) as $node)
- return $node;
- }
- }
-
- /**
- * @return string configuration file.
- */
- protected abstract function getConfigFile();
-}
-
-/**
- * TSqlMapXmlConfig class.
- *
- * Configures the TSqlMapManager using xml configuration file.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder
-{
- /**
- * @var TSqlMapManager manager
- */
- private $_manager;
- /**
- * @var string configuration file.
- */
- private $_configFile;
- /**
- * @var array global properties.
- */
- private $_properties=array();
-
- /**
- * @param TSqlMapManager manager instance.
- */
- public function __construct($manager)
- {
- $this->_manager=$manager;
- }
-
- public function getManager()
- {
- return $this->_manager;
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure the TSqlMapManager using the given xml file.
- * @param string SqlMap configuration xml file.
- */
- public function configure($filename=null)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this);
-
- foreach($document->xpath('//property') as $property)
- $this->loadGlobalProperty($property);
-
- foreach($document->xpath('//typeHandler') as $handler)
- $this->loadTypeHandler($handler);
-
- foreach($document->xpath('//connection[last()]') as $conn)
- $this->loadDatabaseConnection($conn);
-
- //try to load configuration in the current config file.
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $mapping->configure($filename);
-
- foreach($document->xpath('//sqlMap') as $sqlmap)
- $this->loadSqlMappingFiles($sqlmap);
-
- $this->resolveResultMapping();
- $this->attachCacheModels();
- }
-
- /**
- * Load global replacement property.
- * @param SimpleXmlElement property node.
- */
- protected function loadGlobalProperty($node)
- {
- $this->_properties[(string)$node['name']] = (string)$node['value'];
- }
-
- /**
- * Load the type handler configurations.
- * @param SimpleXmlElement type handler node
- */
- protected function loadTypeHandler($node)
- {
- $handler = $this->createObjectFromNode($node);
- $this->_manager->getTypeHandlers()->registerTypeHandler($handler);
- }
-
- /**
- * Load the database connection tag.
- * @param SimpleXmlElement connection node.
- */
- protected function loadDatabaseConnection($node)
- {
- $conn = $this->createObjectFromNode($node);
- $this->_manager->setDbConnection($conn);
- }
-
- /**
- * Load SqlMap mapping configuration.
- * @param unknown_type $node
- */
- protected function loadSqlMappingFiles($node)
- {
- if(strlen($resource = (string)$node['resource']) > 0)
- {
- if( strpos($resource, '${') !== false)
- $resource = $this->replaceProperties($resource);
-
- $mapping = new TSqlMapXmlMappingConfiguration($this);
- $filename = $this->getAbsoluteFilePath($this->_configFile, $resource);
- $mapping->configure($filename);
- }
- }
-
- /**
- * Resolve nest result mappings.
- */
- protected function resolveResultMapping()
- {
- $maps = $this->_manager->getResultMaps();
- foreach($maps as $entry)
- {
- foreach($entry->getColumns() as $item)
- {
- $resultMap = $item->getResultMapping();
- if(strlen($resultMap) > 0)
- {
- if($maps->contains($resultMap))
- $item->setNestedResultMap($maps[$resultMap]);
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_result_mapping',
- $resultMap, $this->_configFile, $entry->getID());
- }
- }
- if($entry->getDiscriminator()!==null)
- $entry->getDiscriminator()->initialize($this->_manager);
- }
- }
-
- /**
- * Set the cache for each statement having a cache model property.
- */
- protected function attachCacheModels()
- {
- foreach($this->_manager->getMappedStatements() as $mappedStatement)
- {
- if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0)
- {
- $cache = $this->_manager->getCacheModel($model);
- $mappedStatement->getStatement()->setCache($cache);
- }
- }
- }
-
- /**
- * Replace the place holders ${name} in text with properties the
- * corresponding global property value.
- * @param string original string.
- * @return string string with global property replacement.
- */
- public function replaceProperties($string)
- {
- foreach($this->_properties as $find => $replace)
- $string = str_replace('${'.$find.'}', $replace, $string);
- return $string;
- }
-}
-
-/**
- * Loads the statements, result maps, parameters maps from xml configuration.
- *
- * description
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap.Configuration
- * @since 3.1
- */
-class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
-{
- private $_xmlConfig;
- private $_configFile;
- private $_manager;
-
- private $_document;
-
- private $_FlushOnExecuteStatements=array();
-
- /**
- * Regular expressions for escaping simple/inline parameter symbols
- */
- const SIMPLE_MARK='$';
- const INLINE_SYMBOL='#';
- const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/';
- const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/';
- const SIMPLE_PLACEHOLDER='`!!`';
- const INLINE_PLACEHOLDER='`!!!`';
-
- /**
- * @param TSqlMapXmlConfiguration parent xml configuration.
- */
- public function __construct(TSqlMapXmlConfiguration $xmlConfig)
- {
- $this->_xmlConfig=$xmlConfig;
- $this->_manager=$xmlConfig->getManager();
- }
-
- protected function getConfigFile()
- {
- return $this->_configFile;
- }
-
- /**
- * Configure an XML mapping.
- * @param string xml mapping filename.
- */
- public function configure($filename)
- {
- $this->_configFile=$filename;
- $document = $this->loadXmlDocument($filename,$this->_xmlConfig);
- $this->_document=$document;
-
- static $bCacheDependencies;
- if($bCacheDependencies === null)
- $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance;
-
- if($bCacheDependencies)
- $this->_manager->getCacheDependencies()
- ->getDependencies()
- ->add(new TFileCacheDependency($filename));
-
- foreach($document->xpath('//resultMap') as $node)
- $this->loadResultMap($node);
-
- foreach($document->xpath('//parameterMap') as $node)
- $this->loadParameterMap($node);
-
- foreach($document->xpath('//statement') as $node)
- $this->loadStatementTag($node);
-
- foreach($document->xpath('//select') as $node)
- $this->loadSelectTag($node);
-
- foreach($document->xpath('//insert') as $node)
- $this->loadInsertTag($node);
-
- foreach($document->xpath('//update') as $node)
- $this->loadUpdateTag($node);
-
- foreach($document->xpath('//delete') as $node)
- $this->loadDeleteTag($node);
-
- foreach($document->xpath('//procedure') as $node)
- $this->loadProcedureTag($node);
-
- foreach($document->xpath('//cacheModel') as $node)
- $this->loadCacheModel($node);
-
- $this->registerCacheTriggers();
- }
-
- /**
- * Load the result maps.
- * @param SimpleXmlElement result map node.
- */
- protected function loadResultMap($node)
- {
- $resultMap = $this->createResultMap($node);
-
- //find extended result map.
- if(strlen($extendMap = $resultMap->getExtends()) > 0)
- {
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap);
- if($extendNode!==null)
- $this->loadResultMap($extendNode);
- }
-
- if(!$this->_manager->getResultMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap);
-
- $superMap = $this->_manager->getResultMap($extendMap);
- $resultMap->getColumns()->mergeWith($superMap->getColumns());
- }
-
- //add the result map
- if(!$this->_manager->getResultMaps()->contains($resultMap->getID()))
- $this->_manager->addResultMap($resultMap);
- }
-
- /**
- * Create a new result map and its associated result properties,
- * disciminiator and sub maps.
- * @param SimpleXmlElement result map node
- * @return TResultMap SqlMap result mapping.
- */
- protected function createResultMap($node)
- {
- $resultMap = new TResultMap();
- $this->setObjectPropFromNode($resultMap,$node);
-
- //result nodes
- foreach($node->result as $result)
- {
- $property = new TResultProperty($resultMap);
- $this->setObjectPropFromNode($property,$result);
- $resultMap->addResultProperty($property);
- }
-
- //create the discriminator
- $discriminator = null;
- if(isset($node->discriminator))
- {
- $discriminator = new TDiscriminator();
- $this->setObjectPropFromNode($discriminator, $node->discriminator);
- $discriminator->initMapping($resultMap);
- }
-
- foreach($node->xpath('subMap') as $subMapNode)
- {
- if($discriminator===null)
- throw new TSqlMapConfigurationException(
- 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
- $subMap = new TSubMap;
- $this->setObjectPropFromNode($subMap,$subMapNode);
- $discriminator->addSubMap($subMap);
- }
-
- if($discriminator!==null)
- $resultMap->setDiscriminator($discriminator);
-
- return $resultMap;
- }
-
- /**
- * Load parameter map from xml.
- *
- * @param SimpleXmlElement parameter map node.
- */
- protected function loadParameterMap($node)
- {
- $parameterMap = $this->createParameterMap($node);
-
- if(strlen($extendMap = $parameterMap->getExtends()) > 0)
- {
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- {
- $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap);
- if($extendNode!==null)
- $this->loadParameterMap($extendNode);
- }
-
- if(!$this->_manager->getParameterMaps()->contains($extendMap))
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap);
- $superMap = $this->_manager->getParameterMap($extendMap);
- $index = 0;
- foreach($superMap->getPropertyNames() as $propertyName)
- $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName));
- }
- $this->_manager->addParameterMap($parameterMap);
- }
-
- /**
- * Create a new parameter map from xml node.
- * @param SimpleXmlElement parameter map node.
- * @return TParameterMap new parameter mapping.
- */
- protected function createParameterMap($node)
- {
- $parameterMap = new TParameterMap();
- $this->setObjectPropFromNode($parameterMap,$node);
- foreach($node->parameter as $parameter)
- {
- $property = new TParameterProperty();
- $this->setObjectPropFromNode($property,$parameter);
- $parameterMap->addProperty($property);
- }
- return $parameterMap;
- }
-
- /**
- * Load statement mapping from xml configuration file.
- * @param SimpleXmlElement statement node.
- */
- protected function loadStatementTag($node)
- {
- $statement = new TSqlMapStatement();
- $this->setObjectPropFromNode($statement,$node);
- $this->processSqlStatement($statement, $node);
- $mappedStatement = new TMappedStatement($this->_manager, $statement);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load extended SQL statements if application. Replaces global properties
- * in the sql text. Extracts inline parameter maps.
- * @param TSqlMapStatement mapped statement.
- * @param SimpleXmlElement statement node.
- */
- protected function processSqlStatement($statement, $node)
- {
- $commandText = (string)$node;
- if(strlen($extend = $statement->getExtends()) > 0)
- {
- $superNode = $this->getElementByIdValue($this->_document,'*',$extend);
- if($superNode!==null)
- $commandText = (string)$superNode . $commandText;
- else
- throw new TSqlMapConfigurationException(
- 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node);
- }
- //$commandText = $this->_xmlConfig->replaceProperties($commandText);
- $statement->initialize($this->_manager);
- $this->applyInlineParameterMap($statement, $commandText, $node);
- }
-
- /**
- * Extract inline parameter maps.
- * @param TSqlMapStatement statement object.
- * @param string sql text
- * @param SimpleXmlElement statement node.
- */
- protected function applyInlineParameterMap($statement, $sqlStatement, $node)
- {
- $scope['file'] = $this->_configFile;
- $scope['node'] = $node;
-
- $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement);
- if($statement->parameterMap() === null)
- {
- // Build a Parametermap with the inline parameters.
- // if they exist. Then delete inline infos from sqltext.
- $parameterParser = new TInlineParameterMapParser;
- $sqlText = $parameterParser->parse($sqlStatement, $scope);
- if(count($sqlText['parameters']) > 0)
- {
- $map = new TParameterMap();
- $map->setID($statement->getID().'-InLineParameterMap');
- $statement->setInlineParameterMap($map);
- foreach($sqlText['parameters'] as $property)
- $map->addProperty($property);
- }
- $sqlStatement = $sqlText['sql'];
- }
- $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement);
-
- $this->prepareSql($statement, $sqlStatement, $node);
- }
-
- /**
- * Prepare the sql text (may extend to dynamic sql).
- * @param TSqlMapStatement mapped statement.
- * @param string sql text.
- * @param SimpleXmlElement statement node.
- * @todo Extend to dynamic sql.
- */
- protected function prepareSql($statement,$sqlStatement, $node)
- {
- $simpleDynamic = new TSimpleDynamicParser;
- $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement);
- $dynamics = $simpleDynamic->parse($sqlStatement);
- if(count($dynamics['parameters']) > 0)
- {
- $sql = new TSimpleDynamicSql($dynamics['parameters']);
- $sqlStatement = $dynamics['sql'];
- }
- else
- $sql = new TStaticSql();
- $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement);
- $sql->buildPreparedStatement($statement, $sqlStatement);
- $statement->setSqlText($sql);
- }
-
- /**
- * Load select statement from xml mapping.
- * @param SimpleXmlElement select node.
- */
- protected function loadSelectTag($node)
- {
- $select = new TSqlMapSelect;
- $this->setObjectPropFromNode($select,$node);
- $this->processSqlStatement($select,$node);
- $mappedStatement = new TMappedStatement($this->_manager, $select);
- if(strlen($select->getCacheModel()) > 0)
- $mappedStatement = new TCachingStatement($mappedStatement);
-
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load insert statement from xml mapping.
- * @param SimpleXmlElement insert node.
- */
- protected function loadInsertTag($node)
- {
- $insert = $this->createInsertStatement($node);
- $this->processSqlStatement($insert, $node);
- $mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Create new insert statement from xml node.
- * @param SimpleXmlElement insert node.
- * @return TSqlMapInsert insert statement.
- */
- protected function createInsertStatement($node)
- {
- $insert = new TSqlMapInsert;
- $this->setObjectPropFromNode($insert,$node);
- if(isset($node->selectKey))
- $this->loadSelectKeyTag($insert,$node->selectKey);
- return $insert;
- }
-
- /**
- * Load the selectKey statement from xml mapping.
- * @param SimpleXmlElement selectkey node
- */
- protected function loadSelectKeyTag($insert, $node)
- {
- $selectKey = new TSqlMapSelectKey;
- $this->setObjectPropFromNode($selectKey,$node);
- $selectKey->setID($insert->getID());
- $selectKey->setID($insert->getID().'.SelectKey');
- $this->processSqlStatement($selectKey,$node);
- $insert->setSelectKey($selectKey);
- $mappedStatement = new TMappedStatement($this->_manager, $selectKey);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load update statement from xml mapping.
- * @param SimpleXmlElement update node.
- */
- protected function loadUpdateTag($node)
- {
- $update = new TSqlMapUpdate;
- $this->setObjectPropFromNode($update,$node);
- $this->processSqlStatement($update, $node);
- $mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load delete statement from xml mapping.
- * @param SimpleXmlElement delete node.
- */
- protected function loadDeleteTag($node)
- {
- $delete = new TSqlMapDelete;
- $this->setObjectPropFromNode($delete,$node);
- $this->processSqlStatement($delete, $node);
- $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
- $this->_manager->addMappedStatement($mappedStatement);
- }
-
- /**
- * Load procedure statement from xml mapping.
- * @todo Implement loading procedure
- * @param SimpleXmlElement procedure node
- */
- protected function loadProcedureTag($node)
- {
- //var_dump('todo: add load procedure');
- }
-
- /**
- * Load cache models from xml mapping.
- * @param SimpleXmlElement cache node.
- */
- protected function loadCacheModel($node)
- {
- $cacheModel = new TSqlMapCacheModel;
- $properties = array('id','implementation');
- foreach($node->attributes() as $name=>$value)
- {
- if(in_array(strtolower($name), $properties))
- $cacheModel->{'set'.$name}((string)$value);
- }
- $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
- $this->setObjectPropFromNode($cache,$node,$properties);
-
- foreach($node->xpath('property') as $propertyNode)
- {
- $name = $propertyNode->attributes()->name;
- if($name===null || $name==='') continue;
-
- $value = $propertyNode->attributes()->value;
- if($value===null || $value==='') continue;
-
- if( !TPropertyAccess::has($cache, $name) ) continue;
-
- TPropertyAccess::set($cache, $name, $value);
- }
-
- $this->loadFlushInterval($cacheModel,$node);
-
- $cacheModel->initialize($cache);
- $this->_manager->addCacheModel($cacheModel);
- foreach($node->xpath('flushOnExecute') as $flush)
- $this->loadFlushOnCache($cacheModel,$node,$flush);
- }
-
- /**
- * Load the flush interval
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement cache node
- */
- protected function loadFlushInterval($cacheModel, $node)
- {
- $flushInterval = $node->xpath('flushInterval');
- if($flushInterval === null || count($flushInterval) === 0) return;
- $duration = 0;
- foreach($flushInterval[0]->attributes() as $name=>$value)
- {
- switch(strToLower($name))
- {
- case 'seconds':
- $duration += (integer)$value;
- break;
- case 'minutes':
- $duration += 60 * (integer)$value;
- break;
- case 'hours':
- $duration += 3600 * (integer)$value;
- break;
- case 'days':
- $duration += 86400 * (integer)$value;
- break;
- case 'duration':
- $duration = (integer)$value;
- break 2; // switch, foreach
- }
- }
- $cacheModel->setFlushInterval($duration);
- }
-
- /**
- * Load the flush on cache properties.
- * @param TSqlMapCacheModel cache model
- * @param SimpleXmlElement parent node.
- * @param SimpleXmlElement flush node.
- */
- protected function loadFlushOnCache($cacheModel,$parent,$node)
- {
- $id = $cacheModel->getID();
- if(!isset($this->_FlushOnExecuteStatements[$id]))
- $this->_FlushOnExecuteStatements[$id] = array();
- foreach($node->attributes() as $name=>$value)
- {
- if(strtolower($name)==='statement')
- $this->_FlushOnExecuteStatements[$id][] = (string)$value;
- }
- }
-
- /**
- * Attach CacheModel to statement and register trigger statements for cache models
- */
- protected function registerCacheTriggers()
- {
- foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs)
- {
- $cacheModel = $this->_manager->getCacheModel($cacheID);
- foreach($statementIDs as $statementID)
- {
- $statement = $this->_manager->getMappedStatement($statementID);
- $cacheModel->registerTriggerStatement($statement);
- }
- }
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+
+Prado::using('System.Data.SqlMap.Configuration.TSqlMapStatement');
+
+/**
+ * TSqlMapXmlConfig class file.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ */
+abstract class TSqlMapXmlConfigBuilder
+{
+ /**
+ * Create an instance of an object give by the attribute named 'class' in the
+ * node and set the properties on the object given by attribute names and values.
+ * @param SimpleXmlNode property node
+ * @return Object new instance of class with class name given by 'class' attribute value.
+ */
+ protected function createObjectFromNode($node)
+ {
+ if(isset($node['class']))
+ {
+ $obj = Prado::createComponent((string)$node['class']);
+ $this->setObjectPropFromNode($obj,$node,array('class'));
+ return $obj;
+ }
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_node_class_undef', $node, $this->getConfigFile());
+ }
+
+ /**
+ * For each attributes (excluding attribute named in $except) set the
+ * property of the $obj given by the name of the attribute with the value
+ * of the attribute.
+ * @param Object object instance
+ * @param SimpleXmlNode property node
+ * @param array exception property name
+ */
+ protected function setObjectPropFromNode($obj,$node,$except=array())
+ {
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(!in_array($name,$except))
+ {
+ if($obj->canSetProperty($name))
+ $obj->{$name} = (string)$value;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_invalid_property', $name, get_class($obj),
+ $node, $this->getConfigFile());
+ }
+ }
+ }
+
+ /**
+ * Gets the filename relative to the basefile.
+ * @param string base filename
+ * @param string relative filename
+ * @return string absolute filename.
+ */
+ protected function getAbsoluteFilePath($basefile,$resource)
+ {
+ $basedir = dirname($basefile);
+ $file = realpath($basedir.DIRECTORY_SEPARATOR.$resource);
+ if(!is_string($file) || !is_file($file))
+ $file = realpath($resource);
+ if(is_string($file) && is_file($file))
+ return $file;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_resource', $resource);
+ }
+
+ /**
+ * Load document using simple xml.
+ * @param string filename.
+ * @return SimpleXmlElement xml document.
+ */
+ protected function loadXmlDocument($filename,TSqlMapXmlConfiguration $config)
+ {
+ if( strpos($filename, '${') !== false)
+ $filename = $config->replaceProperties($filename);
+
+ if(!is_file($filename))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_config', $filename);
+ return simplexml_load_string($config->replaceProperties(file_get_contents($filename)));
+ }
+
+ /**
+ * Get element node by ID value (try for attribute name ID as case insensitive).
+ * @param SimpleXmlDocument $document
+ * @param string tag name.
+ * @param string id value.
+ * @return SimpleXmlElement node if found, null otherwise.
+ */
+ protected function getElementByIdValue($document, $tag, $value)
+ {
+ //hack to allow upper case and lower case attribute names.
+ foreach(array('id','ID','Id', 'iD') as $id)
+ {
+ $xpath = "//{$tag}[@{$id}='{$value}']";
+ foreach($document->xpath($xpath) as $node)
+ return $node;
+ }
+ }
+
+ /**
+ * @return string configuration file.
+ */
+ protected abstract function getConfigFile();
+}
+
+/**
+ * TSqlMapXmlConfig class.
+ *
+ * Configures the TSqlMapManager using xml configuration file.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapXmlConfiguration extends TSqlMapXmlConfigBuilder
+{
+ /**
+ * @var TSqlMapManager manager
+ */
+ private $_manager;
+ /**
+ * @var string configuration file.
+ */
+ private $_configFile;
+ /**
+ * @var array global properties.
+ */
+ private $_properties=array();
+
+ /**
+ * @param TSqlMapManager manager instance.
+ */
+ public function __construct($manager)
+ {
+ $this->_manager=$manager;
+ }
+
+ public function getManager()
+ {
+ return $this->_manager;
+ }
+
+ protected function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * Configure the TSqlMapManager using the given xml file.
+ * @param string SqlMap configuration xml file.
+ */
+ public function configure($filename=null)
+ {
+ $this->_configFile=$filename;
+ $document = $this->loadXmlDocument($filename,$this);
+
+ foreach($document->xpath('//property') as $property)
+ $this->loadGlobalProperty($property);
+
+ foreach($document->xpath('//typeHandler') as $handler)
+ $this->loadTypeHandler($handler);
+
+ foreach($document->xpath('//connection[last()]') as $conn)
+ $this->loadDatabaseConnection($conn);
+
+ //try to load configuration in the current config file.
+ $mapping = new TSqlMapXmlMappingConfiguration($this);
+ $mapping->configure($filename);
+
+ foreach($document->xpath('//sqlMap') as $sqlmap)
+ $this->loadSqlMappingFiles($sqlmap);
+
+ $this->resolveResultMapping();
+ $this->attachCacheModels();
+ }
+
+ /**
+ * Load global replacement property.
+ * @param SimpleXmlElement property node.
+ */
+ protected function loadGlobalProperty($node)
+ {
+ $this->_properties[(string)$node['name']] = (string)$node['value'];
+ }
+
+ /**
+ * Load the type handler configurations.
+ * @param SimpleXmlElement type handler node
+ */
+ protected function loadTypeHandler($node)
+ {
+ $handler = $this->createObjectFromNode($node);
+ $this->_manager->getTypeHandlers()->registerTypeHandler($handler);
+ }
+
+ /**
+ * Load the database connection tag.
+ * @param SimpleXmlElement connection node.
+ */
+ protected function loadDatabaseConnection($node)
+ {
+ $conn = $this->createObjectFromNode($node);
+ $this->_manager->setDbConnection($conn);
+ }
+
+ /**
+ * Load SqlMap mapping configuration.
+ * @param unknown_type $node
+ */
+ protected function loadSqlMappingFiles($node)
+ {
+ if(strlen($resource = (string)$node['resource']) > 0)
+ {
+ if( strpos($resource, '${') !== false)
+ $resource = $this->replaceProperties($resource);
+
+ $mapping = new TSqlMapXmlMappingConfiguration($this);
+ $filename = $this->getAbsoluteFilePath($this->_configFile, $resource);
+ $mapping->configure($filename);
+ }
+ }
+
+ /**
+ * Resolve nest result mappings.
+ */
+ protected function resolveResultMapping()
+ {
+ $maps = $this->_manager->getResultMaps();
+ foreach($maps as $entry)
+ {
+ foreach($entry->getColumns() as $item)
+ {
+ $resultMap = $item->getResultMapping();
+ if(strlen($resultMap) > 0)
+ {
+ if($maps->contains($resultMap))
+ $item->setNestedResultMap($maps[$resultMap]);
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_result_mapping',
+ $resultMap, $this->_configFile, $entry->getID());
+ }
+ }
+ if($entry->getDiscriminator()!==null)
+ $entry->getDiscriminator()->initialize($this->_manager);
+ }
+ }
+
+ /**
+ * Set the cache for each statement having a cache model property.
+ */
+ protected function attachCacheModels()
+ {
+ foreach($this->_manager->getMappedStatements() as $mappedStatement)
+ {
+ if(strlen($model = $mappedStatement->getStatement()->getCacheModel()) > 0)
+ {
+ $cache = $this->_manager->getCacheModel($model);
+ $mappedStatement->getStatement()->setCache($cache);
+ }
+ }
+ }
+
+ /**
+ * Replace the place holders ${name} in text with properties the
+ * corresponding global property value.
+ * @param string original string.
+ * @return string string with global property replacement.
+ */
+ public function replaceProperties($string)
+ {
+ foreach($this->_properties as $find => $replace)
+ $string = str_replace('${'.$find.'}', $replace, $string);
+ return $string;
+ }
+}
+
+/**
+ * Loads the statements, result maps, parameters maps from xml configuration.
+ *
+ * description
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap.Configuration
+ * @since 3.1
+ */
+class TSqlMapXmlMappingConfiguration extends TSqlMapXmlConfigBuilder
+{
+ private $_xmlConfig;
+ private $_configFile;
+ private $_manager;
+
+ private $_document;
+
+ private $_FlushOnExecuteStatements=array();
+
+ /**
+ * Regular expressions for escaping simple/inline parameter symbols
+ */
+ const SIMPLE_MARK='$';
+ const INLINE_SYMBOL='#';
+ const ESCAPED_SIMPLE_MARK_REGEXP='/\$\$/';
+ const ESCAPED_INLINE_SYMBOL_REGEXP='/\#\#/';
+ const SIMPLE_PLACEHOLDER='`!!`';
+ const INLINE_PLACEHOLDER='`!!!`';
+
+ /**
+ * @param TSqlMapXmlConfiguration parent xml configuration.
+ */
+ public function __construct(TSqlMapXmlConfiguration $xmlConfig)
+ {
+ $this->_xmlConfig=$xmlConfig;
+ $this->_manager=$xmlConfig->getManager();
+ }
+
+ protected function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * Configure an XML mapping.
+ * @param string xml mapping filename.
+ */
+ public function configure($filename)
+ {
+ $this->_configFile=$filename;
+ $document = $this->loadXmlDocument($filename,$this->_xmlConfig);
+ $this->_document=$document;
+
+ static $bCacheDependencies;
+ if($bCacheDependencies === null)
+ $bCacheDependencies = Prado::getApplication()->getMode() !== TApplicationMode::Performance;
+
+ if($bCacheDependencies)
+ $this->_manager->getCacheDependencies()
+ ->getDependencies()
+ ->add(new TFileCacheDependency($filename));
+
+ foreach($document->xpath('//resultMap') as $node)
+ $this->loadResultMap($node);
+
+ foreach($document->xpath('//parameterMap') as $node)
+ $this->loadParameterMap($node);
+
+ foreach($document->xpath('//statement') as $node)
+ $this->loadStatementTag($node);
+
+ foreach($document->xpath('//select') as $node)
+ $this->loadSelectTag($node);
+
+ foreach($document->xpath('//insert') as $node)
+ $this->loadInsertTag($node);
+
+ foreach($document->xpath('//update') as $node)
+ $this->loadUpdateTag($node);
+
+ foreach($document->xpath('//delete') as $node)
+ $this->loadDeleteTag($node);
+
+ foreach($document->xpath('//procedure') as $node)
+ $this->loadProcedureTag($node);
+
+ foreach($document->xpath('//cacheModel') as $node)
+ $this->loadCacheModel($node);
+
+ $this->registerCacheTriggers();
+ }
+
+ /**
+ * Load the result maps.
+ * @param SimpleXmlElement result map node.
+ */
+ protected function loadResultMap($node)
+ {
+ $resultMap = $this->createResultMap($node);
+
+ //find extended result map.
+ if(strlen($extendMap = $resultMap->getExtends()) > 0)
+ {
+ if(!$this->_manager->getResultMaps()->contains($extendMap))
+ {
+ $extendNode=$this->getElementByIdValue($this->_document,'resultMap',$extendMap);
+ if($extendNode!==null)
+ $this->loadResultMap($extendNode);
+ }
+
+ if(!$this->_manager->getResultMaps()->contains($extendMap))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_result_map', $node, $this->_configFile, $extendMap);
+
+ $superMap = $this->_manager->getResultMap($extendMap);
+ $resultMap->getColumns()->mergeWith($superMap->getColumns());
+ }
+
+ //add the result map
+ if(!$this->_manager->getResultMaps()->contains($resultMap->getID()))
+ $this->_manager->addResultMap($resultMap);
+ }
+
+ /**
+ * Create a new result map and its associated result properties,
+ * disciminiator and sub maps.
+ * @param SimpleXmlElement result map node
+ * @return TResultMap SqlMap result mapping.
+ */
+ protected function createResultMap($node)
+ {
+ $resultMap = new TResultMap();
+ $this->setObjectPropFromNode($resultMap,$node);
+
+ //result nodes
+ foreach($node->result as $result)
+ {
+ $property = new TResultProperty($resultMap);
+ $this->setObjectPropFromNode($property,$result);
+ $resultMap->addResultProperty($property);
+ }
+
+ //create the discriminator
+ $discriminator = null;
+ if(isset($node->discriminator))
+ {
+ $discriminator = new TDiscriminator();
+ $this->setObjectPropFromNode($discriminator, $node->discriminator);
+ $discriminator->initMapping($resultMap);
+ }
+
+ foreach($node->xpath('subMap') as $subMapNode)
+ {
+ if($discriminator===null)
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_undefined_discriminator', $node, $this->_configFile,$subMapNode);
+ $subMap = new TSubMap;
+ $this->setObjectPropFromNode($subMap,$subMapNode);
+ $discriminator->addSubMap($subMap);
+ }
+
+ if($discriminator!==null)
+ $resultMap->setDiscriminator($discriminator);
+
+ return $resultMap;
+ }
+
+ /**
+ * Load parameter map from xml.
+ *
+ * @param SimpleXmlElement parameter map node.
+ */
+ protected function loadParameterMap($node)
+ {
+ $parameterMap = $this->createParameterMap($node);
+
+ if(strlen($extendMap = $parameterMap->getExtends()) > 0)
+ {
+ if(!$this->_manager->getParameterMaps()->contains($extendMap))
+ {
+ $extendNode=$this->getElementByIdValue($this->_document,'parameterMap',$extendMap);
+ if($extendNode!==null)
+ $this->loadParameterMap($extendNode);
+ }
+
+ if(!$this->_manager->getParameterMaps()->contains($extendMap))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_parameter_map', $node, $this->_configFile,$extendMap);
+ $superMap = $this->_manager->getParameterMap($extendMap);
+ $index = 0;
+ foreach($superMap->getPropertyNames() as $propertyName)
+ $parameterMap->insertProperty($index++,$superMap->getProperty($propertyName));
+ }
+ $this->_manager->addParameterMap($parameterMap);
+ }
+
+ /**
+ * Create a new parameter map from xml node.
+ * @param SimpleXmlElement parameter map node.
+ * @return TParameterMap new parameter mapping.
+ */
+ protected function createParameterMap($node)
+ {
+ $parameterMap = new TParameterMap();
+ $this->setObjectPropFromNode($parameterMap,$node);
+ foreach($node->parameter as $parameter)
+ {
+ $property = new TParameterProperty();
+ $this->setObjectPropFromNode($property,$parameter);
+ $parameterMap->addProperty($property);
+ }
+ return $parameterMap;
+ }
+
+ /**
+ * Load statement mapping from xml configuration file.
+ * @param SimpleXmlElement statement node.
+ */
+ protected function loadStatementTag($node)
+ {
+ $statement = new TSqlMapStatement();
+ $this->setObjectPropFromNode($statement,$node);
+ $this->processSqlStatement($statement, $node);
+ $mappedStatement = new TMappedStatement($this->_manager, $statement);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load extended SQL statements if application. Replaces global properties
+ * in the sql text. Extracts inline parameter maps.
+ * @param TSqlMapStatement mapped statement.
+ * @param SimpleXmlElement statement node.
+ */
+ protected function processSqlStatement($statement, $node)
+ {
+ $commandText = (string)$node;
+ if(strlen($extend = $statement->getExtends()) > 0)
+ {
+ $superNode = $this->getElementByIdValue($this->_document,'*',$extend);
+ if($superNode!==null)
+ $commandText = (string)$superNode . $commandText;
+ else
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_unable_to_find_parent_sql', $extend, $this->_configFile,$node);
+ }
+ //$commandText = $this->_xmlConfig->replaceProperties($commandText);
+ $statement->initialize($this->_manager);
+ $this->applyInlineParameterMap($statement, $commandText, $node);
+ }
+
+ /**
+ * Extract inline parameter maps.
+ * @param TSqlMapStatement statement object.
+ * @param string sql text
+ * @param SimpleXmlElement statement node.
+ */
+ protected function applyInlineParameterMap($statement, $sqlStatement, $node)
+ {
+ $scope['file'] = $this->_configFile;
+ $scope['node'] = $node;
+
+ $sqlStatement=preg_replace(self::ESCAPED_INLINE_SYMBOL_REGEXP,self::INLINE_PLACEHOLDER,$sqlStatement);
+ if($statement->parameterMap() === null)
+ {
+ // Build a Parametermap with the inline parameters.
+ // if they exist. Then delete inline infos from sqltext.
+ $parameterParser = new TInlineParameterMapParser;
+ $sqlText = $parameterParser->parse($sqlStatement, $scope);
+ if(count($sqlText['parameters']) > 0)
+ {
+ $map = new TParameterMap();
+ $map->setID($statement->getID().'-InLineParameterMap');
+ $statement->setInlineParameterMap($map);
+ foreach($sqlText['parameters'] as $property)
+ $map->addProperty($property);
+ }
+ $sqlStatement = $sqlText['sql'];
+ }
+ $sqlStatement=preg_replace('/'.self::INLINE_PLACEHOLDER.'/',self::INLINE_SYMBOL,$sqlStatement);
+
+ $this->prepareSql($statement, $sqlStatement, $node);
+ }
+
+ /**
+ * Prepare the sql text (may extend to dynamic sql).
+ * @param TSqlMapStatement mapped statement.
+ * @param string sql text.
+ * @param SimpleXmlElement statement node.
+ * @todo Extend to dynamic sql.
+ */
+ protected function prepareSql($statement,$sqlStatement, $node)
+ {
+ $simpleDynamic = new TSimpleDynamicParser;
+ $sqlStatement=preg_replace(self::ESCAPED_SIMPLE_MARK_REGEXP,self::SIMPLE_PLACEHOLDER,$sqlStatement);
+ $dynamics = $simpleDynamic->parse($sqlStatement);
+ if(count($dynamics['parameters']) > 0)
+ {
+ $sql = new TSimpleDynamicSql($dynamics['parameters']);
+ $sqlStatement = $dynamics['sql'];
+ }
+ else
+ $sql = new TStaticSql();
+ $sqlStatement=preg_replace('/'.self::SIMPLE_PLACEHOLDER.'/',self::SIMPLE_MARK,$sqlStatement);
+ $sql->buildPreparedStatement($statement, $sqlStatement);
+ $statement->setSqlText($sql);
+ }
+
+ /**
+ * Load select statement from xml mapping.
+ * @param SimpleXmlElement select node.
+ */
+ protected function loadSelectTag($node)
+ {
+ $select = new TSqlMapSelect;
+ $this->setObjectPropFromNode($select,$node);
+ $this->processSqlStatement($select,$node);
+ $mappedStatement = new TMappedStatement($this->_manager, $select);
+ if(strlen($select->getCacheModel()) > 0)
+ $mappedStatement = new TCachingStatement($mappedStatement);
+
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load insert statement from xml mapping.
+ * @param SimpleXmlElement insert node.
+ */
+ protected function loadInsertTag($node)
+ {
+ $insert = $this->createInsertStatement($node);
+ $this->processSqlStatement($insert, $node);
+ $mappedStatement = new TInsertMappedStatement($this->_manager, $insert);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Create new insert statement from xml node.
+ * @param SimpleXmlElement insert node.
+ * @return TSqlMapInsert insert statement.
+ */
+ protected function createInsertStatement($node)
+ {
+ $insert = new TSqlMapInsert;
+ $this->setObjectPropFromNode($insert,$node);
+ if(isset($node->selectKey))
+ $this->loadSelectKeyTag($insert,$node->selectKey);
+ return $insert;
+ }
+
+ /**
+ * Load the selectKey statement from xml mapping.
+ * @param SimpleXmlElement selectkey node
+ */
+ protected function loadSelectKeyTag($insert, $node)
+ {
+ $selectKey = new TSqlMapSelectKey;
+ $this->setObjectPropFromNode($selectKey,$node);
+ $selectKey->setID($insert->getID());
+ $selectKey->setID($insert->getID().'.SelectKey');
+ $this->processSqlStatement($selectKey,$node);
+ $insert->setSelectKey($selectKey);
+ $mappedStatement = new TMappedStatement($this->_manager, $selectKey);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load update statement from xml mapping.
+ * @param SimpleXmlElement update node.
+ */
+ protected function loadUpdateTag($node)
+ {
+ $update = new TSqlMapUpdate;
+ $this->setObjectPropFromNode($update,$node);
+ $this->processSqlStatement($update, $node);
+ $mappedStatement = new TUpdateMappedStatement($this->_manager, $update);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load delete statement from xml mapping.
+ * @param SimpleXmlElement delete node.
+ */
+ protected function loadDeleteTag($node)
+ {
+ $delete = new TSqlMapDelete;
+ $this->setObjectPropFromNode($delete,$node);
+ $this->processSqlStatement($delete, $node);
+ $mappedStatement = new TDeleteMappedStatement($this->_manager, $delete);
+ $this->_manager->addMappedStatement($mappedStatement);
+ }
+
+ /**
+ * Load procedure statement from xml mapping.
+ * @todo Implement loading procedure
+ * @param SimpleXmlElement procedure node
+ */
+ protected function loadProcedureTag($node)
+ {
+ //var_dump('todo: add load procedure');
+ }
+
+ /**
+ * Load cache models from xml mapping.
+ * @param SimpleXmlElement cache node.
+ */
+ protected function loadCacheModel($node)
+ {
+ $cacheModel = new TSqlMapCacheModel;
+ $properties = array('id','implementation');
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(in_array(strtolower($name), $properties))
+ $cacheModel->{'set'.$name}((string)$value);
+ }
+ $cache = Prado::createComponent($cacheModel->getImplementationClass(), $cacheModel);
+ $this->setObjectPropFromNode($cache,$node,$properties);
+
+ foreach($node->xpath('property') as $propertyNode)
+ {
+ $name = $propertyNode->attributes()->name;
+ if($name===null || $name==='') continue;
+
+ $value = $propertyNode->attributes()->value;
+ if($value===null || $value==='') continue;
+
+ if( !TPropertyAccess::has($cache, $name) ) continue;
+
+ TPropertyAccess::set($cache, $name, $value);
+ }
+
+ $this->loadFlushInterval($cacheModel,$node);
+
+ $cacheModel->initialize($cache);
+ $this->_manager->addCacheModel($cacheModel);
+ foreach($node->xpath('flushOnExecute') as $flush)
+ $this->loadFlushOnCache($cacheModel,$node,$flush);
+ }
+
+ /**
+ * Load the flush interval
+ * @param TSqlMapCacheModel cache model
+ * @param SimpleXmlElement cache node
+ */
+ protected function loadFlushInterval($cacheModel, $node)
+ {
+ $flushInterval = $node->xpath('flushInterval');
+ if($flushInterval === null || count($flushInterval) === 0) return;
+ $duration = 0;
+ foreach($flushInterval[0]->attributes() as $name=>$value)
+ {
+ switch(strToLower($name))
+ {
+ case 'seconds':
+ $duration += (integer)$value;
+ break;
+ case 'minutes':
+ $duration += 60 * (integer)$value;
+ break;
+ case 'hours':
+ $duration += 3600 * (integer)$value;
+ break;
+ case 'days':
+ $duration += 86400 * (integer)$value;
+ break;
+ case 'duration':
+ $duration = (integer)$value;
+ break 2; // switch, foreach
+ }
+ }
+ $cacheModel->setFlushInterval($duration);
+ }
+
+ /**
+ * Load the flush on cache properties.
+ * @param TSqlMapCacheModel cache model
+ * @param SimpleXmlElement parent node.
+ * @param SimpleXmlElement flush node.
+ */
+ protected function loadFlushOnCache($cacheModel,$parent,$node)
+ {
+ $id = $cacheModel->getID();
+ if(!isset($this->_FlushOnExecuteStatements[$id]))
+ $this->_FlushOnExecuteStatements[$id] = array();
+ foreach($node->attributes() as $name=>$value)
+ {
+ if(strtolower($name)==='statement')
+ $this->_FlushOnExecuteStatements[$id][] = (string)$value;
+ }
+ }
+
+ /**
+ * Attach CacheModel to statement and register trigger statements for cache models
+ */
+ protected function registerCacheTriggers()
+ {
+ foreach($this->_FlushOnExecuteStatements as $cacheID => $statementIDs)
+ {
+ $cacheModel = $this->_manager->getCacheModel($cacheID);
+ foreach($statementIDs as $statementID)
+ {
+ $statement = $this->_manager->getMappedStatement($statementID);
+ $cacheModel->registerTriggerStatement($statement);
+ }
+ }
+ }
+}
+
diff --git a/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php b/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php
index d780b413..e85c54cf 100644
--- a/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php
+++ b/framework/Data/SqlMap/DataMapper/TFastSqlMapApplicationCache.php
@@ -1,89 +1,89 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
- * @package System.Data.SqlMap
- */
-
-/**
- * TFastSqlMapApplicationCache class file
- *
- * Fast SqlMap result cache class with minimal-concurrency get/set and atomic flush operations
- *
- * @author Berczi Gabor
- * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
- * @package System.Data.SqlMap
- * @since 3.2
- */
-
-class TFastSqlMapApplicationCache implements ICache
-{
- protected $_cacheModel=null;
- protected $_cache=null;
-
- public function __construct($cacheModel=null)
- {
- $this->_cacheModel = $cacheModel;
- }
-
- protected function getBaseKeyKeyName()
- {
- return 'SqlMapCacheBaseKey::'.$this->_cacheModel->getId();
- }
-
- protected function getBaseKey()
- {
- $cache = $this->getCache();
- $keyname = $this->getBaseKeyKeyName();
- $basekey = $cache->get($keyname);
- if (!$basekey)
- {
- $basekey = DxUtil::generateRandomHash(8);
- $cache->set($keyname,$basekey);
- }
- return $basekey;
- }
-
- protected function getCacheKey($key)
- {
- return $this->getBaseKey().'###'.$key;
- }
-
- public function delete($key)
- {
- $this->getCache()->delete($this->getCacheKey($key));
- }
-
- public function flush()
- {
- $this->getCache()->delete($this->getBaseKeyKeyName());
- }
-
- public function get($key)
- {
- $result = $this->getCache()->get($this->getCacheKey($key));
- return $result === false ? null : $result;
- }
-
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->getCache()->set($this->getCacheKey($key), $value, $expire,$dependency);
- }
-
- protected function getCache()
- {
- if (!$this->_cache)
- $this->_cache = Prado::getApplication()->getCache();
- return $this->_cache;
- }
-
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
+ * @package System.Data.SqlMap
+ */
+
+/**
+ * TFastSqlMapApplicationCache class file
+ *
+ * Fast SqlMap result cache class with minimal-concurrency get/set and atomic flush operations
+ *
+ * @author Berczi Gabor
+ * @version $Id: TFastSqlMapApplicationCache.php 2996 2011-06-20 15:24:57Z ctrlaltca@gmail.com $
+ * @package System.Data.SqlMap
+ * @since 3.2
+ */
+
+class TFastSqlMapApplicationCache implements ICache
+{
+ protected $_cacheModel=null;
+ protected $_cache=null;
+
+ public function __construct($cacheModel=null)
+ {
+ $this->_cacheModel = $cacheModel;
+ }
+
+ protected function getBaseKeyKeyName()
+ {
+ return 'SqlMapCacheBaseKey::'.$this->_cacheModel->getId();
+ }
+
+ protected function getBaseKey()
+ {
+ $cache = $this->getCache();
+ $keyname = $this->getBaseKeyKeyName();
+ $basekey = $cache->get($keyname);
+ if (!$basekey)
+ {
+ $basekey = DxUtil::generateRandomHash(8);
+ $cache->set($keyname,$basekey);
+ }
+ return $basekey;
+ }
+
+ protected function getCacheKey($key)
+ {
+ return $this->getBaseKey().'###'.$key;
+ }
+
+ public function delete($key)
+ {
+ $this->getCache()->delete($this->getCacheKey($key));
+ }
+
+ public function flush()
+ {
+ $this->getCache()->delete($this->getBaseKeyKeyName());
+ }
+
+ public function get($key)
+ {
+ $result = $this->getCache()->get($this->getCacheKey($key));
+ return $result === false ? null : $result;
+ }
+
+ public function set($key, $value,$expire=0,$dependency=null)
+ {
+ $this->getCache()->set($this->getCacheKey($key), $value, $expire,$dependency);
+ }
+
+ protected function getCache()
+ {
+ if (!$this->_cache)
+ $this->_cache = Prado::getApplication()->getCache();
+ return $this->_cache;
+ }
+
+ public function add($id,$value,$expire=0,$dependency=null)
+ {
+ throw new TSqlMapException('sqlmap_use_set_to_store_cache');
+ }
+}
diff --git a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
index 9b163960..8e5d8d85 100644
--- a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
+++ b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
@@ -1,144 +1,144 @@
-
- * @link http://www.pradosoft.com/
+
+ * @link http://www.pradosoft.com/
* @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * TLazyLoadList executes mapped statements when the proxy collection is first accessed.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TLazyLoadList
-{
- private $_param;
- private $_target;
- private $_propertyName='';
- private $_statement='';
- private $_loaded=false;
- private $_innerList;
- private $_connection;
-
- /**
- * Create a new proxy list that will execute the mapped statement when any
- * of the list's method are accessed for the first time.
- * @param TMappedStatement statement to be executed to load the data.
- * @param mixed parameter value for the statement.
- * @param object result object that contains the lazy collection.
- * @param string property of the result object to set the loaded collection.
- */
- protected function __construct($mappedStatement, $param, $target, $propertyName)
- {
- $this->_param = $param;
- $this->_target = $target;
- $this->_statement = $mappedStatement;
- $this->_connection=$mappedStatement->getManager()->getDbConnection();
- $this->_propertyName = $propertyName;
- }
-
- /**
- * Create a new instance of a lazy collection.
- * @param TMappedStatement statement to be executed to load the data.
- * @param mixed parameter value for the statement.
- * @param object result object that contains the lazy collection.
- * @param string property of the result object to set the loaded collection.
- * @return TObjectProxy proxied collection object.
- */
- public static function newInstance($mappedStatement, $param, $target, $propertyName)
- {
- $handler = new self($mappedStatement, $param, $target, $propertyName);
- $statement = $mappedStatement->getStatement();
- $registry=$mappedStatement->getManager()->getTypeHandlers();
- $list = $statement->createInstanceOfListClass($registry);
- if(!is_object($list))
- throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID());
- return new TObjectProxy($handler, $list);
- }
-
- /**
- * Relay the method call to the underlying collection.
- * @param string method name.
- * @param array method parameters.
- */
- public function intercept($method, $arguments)
- {
- return call_user_func_array(array($this->_innerList, $method), $arguments);
- }
-
- /**
- * Load the data by executing the mapped statement.
- */
- protected function fetchListData()
- {
- if($this->_loaded == false)
- {
- $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param);
- $this->_loaded = true;
- //replace the target property with real list
- TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList);
- }
- }
-
- /**
- * Try to fetch the data when any of the proxy collection method is called.
- * @param string method name.
- * @return boolean true if the underlying collection has the corresponding method name.
- */
- public function hasMethod($method)
- {
- $this->fetchListData();
- if(is_object($this->_innerList))
- return in_array($method, get_class_methods($this->_innerList));
- return false;
- }
-}
-
-/**
- * TObjectProxy sets up a simple object that intercepts method calls to a
- * particular object and relays the call to handler object.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TObjectProxy
-{
- private $_object;
- private $_handler;
-
- /**
- * @param object handler to method calls.
- * @param object the object to by proxied.
- */
- public function __construct($handler, $object)
- {
- $this->_handler = $handler;
- $this->_object = $object;
- }
-
- /**
- * Relay the method call to the handler object (if able to be handled), otherwise
- * it calls the proxied object's method.
- * @param string method name called
- * @param array method arguments
- * @return mixed method return value.
- */
- public function __call($method,$params)
- {
- if($this->_handler->hasMethod($method))
- return $this->_handler->intercept($method, $params);
- else
- return call_user_func_array(array($this->_object, $method), $params);
- }
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap
+ */
+
+/**
+ * TLazyLoadList executes mapped statements when the proxy collection is first accessed.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TLazyLoadList
+{
+ private $_param;
+ private $_target;
+ private $_propertyName='';
+ private $_statement='';
+ private $_loaded=false;
+ private $_innerList;
+ private $_connection;
+
+ /**
+ * Create a new proxy list that will execute the mapped statement when any
+ * of the list's method are accessed for the first time.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ */
+ protected function __construct($mappedStatement, $param, $target, $propertyName)
+ {
+ $this->_param = $param;
+ $this->_target = $target;
+ $this->_statement = $mappedStatement;
+ $this->_connection=$mappedStatement->getManager()->getDbConnection();
+ $this->_propertyName = $propertyName;
+ }
+
+ /**
+ * Create a new instance of a lazy collection.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ * @return TObjectProxy proxied collection object.
+ */
+ public static function newInstance($mappedStatement, $param, $target, $propertyName)
+ {
+ $handler = new self($mappedStatement, $param, $target, $propertyName);
+ $statement = $mappedStatement->getStatement();
+ $registry=$mappedStatement->getManager()->getTypeHandlers();
+ $list = $statement->createInstanceOfListClass($registry);
+ if(!is_object($list))
+ throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID());
+ return new TObjectProxy($handler, $list);
+ }
+
+ /**
+ * Relay the method call to the underlying collection.
+ * @param string method name.
+ * @param array method parameters.
+ */
+ public function intercept($method, $arguments)
+ {
+ return call_user_func_array(array($this->_innerList, $method), $arguments);
+ }
+
+ /**
+ * Load the data by executing the mapped statement.
+ */
+ protected function fetchListData()
+ {
+ if($this->_loaded == false)
+ {
+ $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param);
+ $this->_loaded = true;
+ //replace the target property with real list
+ TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList);
+ }
+ }
+
+ /**
+ * Try to fetch the data when any of the proxy collection method is called.
+ * @param string method name.
+ * @return boolean true if the underlying collection has the corresponding method name.
+ */
+ public function hasMethod($method)
+ {
+ $this->fetchListData();
+ if(is_object($this->_innerList))
+ return in_array($method, get_class_methods($this->_innerList));
+ return false;
+ }
+}
+
+/**
+ * TObjectProxy sets up a simple object that intercepts method calls to a
+ * particular object and relays the call to handler object.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TObjectProxy
+{
+ private $_object;
+ private $_handler;
+
+ /**
+ * @param object handler to method calls.
+ * @param object the object to by proxied.
+ */
+ public function __construct($handler, $object)
+ {
+ $this->_handler = $handler;
+ $this->_object = $object;
+ }
+
+ /**
+ * Relay the method call to the handler object (if able to be handled), otherwise
+ * it calls the proxied object's method.
+ * @param string method name called
+ * @param array method arguments
+ * @return mixed method return value.
+ */
+ public function __call($method,$params)
+ {
+ if($this->_handler->hasMethod($method))
+ return $this->_handler->intercept($method, $params);
+ else
+ return call_user_func_array(array($this->_object, $method), $params);
+ }
+}
+
diff --git a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
index 7445e9d8..a6a2c451 100644
--- a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
+++ b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
@@ -1,156 +1,156 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * TPropertyAccess class provides dot notation stype property access and setting.
- *
- * Access object's properties (and subproperties) using dot path notation.
- * The following are equivalent.
- *
- * echo $obj->property1;
- * echo $obj->getProperty1();
- * echo $obj['property1']; //$obj may be an array or object
- * echo TPropertyAccess($obj, 'property1');
- *
- *
- * Setting a property value.
- *
- * $obj1->propert1 = 'hello';
- * $obj->setProperty('hello');
- * $obj['property1'] = 'hello'; //$obj may be an array or object
- * TPropertyAccess($obj, 'property1', 'hello');
- *
- *
- * Subproperties are supported using the dot notation. E.g.
- *
- * echo $obj->property1->property2->property3
- * echo TPropertyAccess::get($obj, 'property1.property2.property3');
- *
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TPropertyAccess
-{
- /**
- * Gets the property value.
- * @param mixed object or path.
- * @param string property path.
- * @return mixed property value.
- * @throws TInvalidDataValueException if property path is invalid.
- */
- public static function get($object,$path)
- {
- if(!is_array($object) && !is_object($object))
- return $object;
- $properties = explode('.', $path);
- foreach($properties as $prop)
- {
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- if(array_key_exists($prop, $object))
- $object = $object[$prop];
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- else if(is_object($object))
- {
- $getter = 'get'.$prop;
- if(method_exists($object, $getter) && is_callable(array($object, $getter)))
- $object = $object->{$getter}();
- else if(in_array($prop, array_keys(get_object_vars($object))))
- $object = $object->{$prop};
- elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
- $object = $object->{$prop};
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
- }
- return $object;
- }
-
- /**
- * @param mixed object or array
- * @param string property path.
- * @return boolean true if property path is valid
- */
- public static function has($object, $path)
- {
- if(!is_array($object) && !is_object($object))
- return false;
- $properties = explode('.', $path);
- foreach($properties as $prop)
- {
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- if(array_key_exists($prop, $object))
- $object = $object[$prop];
- else
- return false;
- }
- else if(is_object($object))
- {
- $getter = 'get'.$prop;
- if(method_exists($object, $getter) && is_callable(array($object, $getter)))
- $object = $object->{$getter}();
- else if(in_array($prop, array_keys(get_object_vars($object))))
- $object = $object->{$prop};
- elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
- $object = $object->{$prop};
- else
- return false;
- }
- else
- return false;
- }
- return true;
- }
-
- /**
- * Sets the property value.
- * @param mixed object or array
- * @param string property path.
- * @param mixed new property value.
- * @throws TInvalidDataValueException if property path is invalid.
- */
- public static function set(&$originalObject, $path, $value)
- {
- $properties = explode('.', $path);
- $prop = array_pop($properties);
- if(count($properties) > 0)
- $object = self::get($originalObject, implode('.',$properties));
- else
- $object = &$originalObject;
-
- if(is_array($object) || $object instanceof ArrayAccess)
- {
- $object[$prop] = $value;
- }
- else if(is_object($object))
- {
- $setter = 'set'.$prop;
- if (method_exists($object, $setter) && is_callable(array($object, $setter)))
- $object->{$setter}($value);
- else
- $object->{$prop} = $value;
- }
- else
- throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path);
- }
-
-}
-
-?>
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap
+ */
+
+/**
+ * TPropertyAccess class provides dot notation stype property access and setting.
+ *
+ * Access object's properties (and subproperties) using dot path notation.
+ * The following are equivalent.
+ *
+ * echo $obj->property1;
+ * echo $obj->getProperty1();
+ * echo $obj['property1']; //$obj may be an array or object
+ * echo TPropertyAccess($obj, 'property1');
+ *
+ *
+ * Setting a property value.
+ *
+ * $obj1->propert1 = 'hello';
+ * $obj->setProperty('hello');
+ * $obj['property1'] = 'hello'; //$obj may be an array or object
+ * TPropertyAccess($obj, 'property1', 'hello');
+ *
+ *
+ * Subproperties are supported using the dot notation. E.g.
+ *
+ * echo $obj->property1->property2->property3
+ * echo TPropertyAccess::get($obj, 'property1.property2.property3');
+ *
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TPropertyAccess
+{
+ /**
+ * Gets the property value.
+ * @param mixed object or path.
+ * @param string property path.
+ * @return mixed property value.
+ * @throws TInvalidDataValueException if property path is invalid.
+ */
+ public static function get($object,$path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return $object;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(method_exists($object, $getter) && is_callable(array($object, $getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
+ $object = $object->{$prop};
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ return $object;
+ }
+
+ /**
+ * @param mixed object or array
+ * @param string property path.
+ * @return boolean true if property path is valid
+ */
+ public static function has($object, $path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return false;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ return false;
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(method_exists($object, $getter) && is_callable(array($object, $getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ elseif(method_exists($object, '__get') && is_callable(array($object, '__get')))
+ $object = $object->{$prop};
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Sets the property value.
+ * @param mixed object or array
+ * @param string property path.
+ * @param mixed new property value.
+ * @throws TInvalidDataValueException if property path is invalid.
+ */
+ public static function set(&$originalObject, $path, $value)
+ {
+ $properties = explode('.', $path);
+ $prop = array_pop($properties);
+ if(count($properties) > 0)
+ $object = self::get($originalObject, implode('.',$properties));
+ else
+ $object = &$originalObject;
+
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ $object[$prop] = $value;
+ }
+ else if(is_object($object))
+ {
+ $setter = 'set'.$prop;
+ if (method_exists($object, $setter) && is_callable(array($object, $setter)))
+ $object->{$setter}($value);
+ else
+ $object->{$prop} = $value;
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path);
+ }
+
+}
+
+?>
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
index db027013..4e713fea 100644
--- a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
@@ -1,295 +1,295 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-/**
- * Allow different implementation of caching strategy. See TSqlMapFifoCache
- * for a first-in-first-out implementation. See TSqlMapLruCache for
- * a least-recently-used cache implementation.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-abstract class TSqlMapCache implements ICache
-{
- protected $_keyList;
- protected $_cache;
- protected $_cacheSize = 100;
- protected $_cacheModel = null;
-
- /**
- * Create a new cache with limited cache size.
- * @param TSqlMapCacheModel $cacheModel.
- */
- public function __construct($cacheModel=null)
- {
- $this->_cache = new TMap;
- $this->_keyList = new TList;
- $this->_cacheModel=$cacheModel;
- }
-
- /**
- * Maximum number of items to cache. Default size is 100.
- * @param int cache size.
- */
- public function setCacheSize($value)
- {
- $this->_cacheSize=TPropertyValue::ensureInteger($value,100);
- }
-
- /**
- * @return int cache size.
- */
- public function getCacheSize()
- {
- return $this->_cacheSize;
- }
-
- /**
- * @return object the object removed if exists, null otherwise.
- */
- public function delete($key)
- {
- $object = $this->get($key);
- $this->_cache->remove($key);
- $this->_keyList->remove($key);
- return $object;
- }
-
- /**
- * Clears the cache.
- */
- public function flush()
- {
- $this->_keyList->clear();
- $this->_cache->clear();
- }
-
- /**
- * @throws TSqlMapException not implemented.
- */
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
-
-/**
- * First-in-First-out cache implementation, removes
- * object that was first added when the cache is full.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapFifoCache extends TSqlMapCache
-{
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- return $this->_cache->itemAt($key);
- }
-
- /**
- * Stores a value identified by a key into cache.
- * The expire and dependency parameters are ignored.
- * @param string cache key
- * @param mixed value to cache.
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->_cache->add($key, $value);
- $this->_keyList->add($key);
- if($this->_keyList->getCount() > $this->_cacheSize)
- {
- $oldestKey = $this->_keyList->removeAt(0);
- $this->_cache->remove($oldestKey);
- }
- }
-}
-
-/**
- * Least recently used cache implementation, removes
- * object that was accessed last when the cache is full.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapLruCache extends TSqlMapCache
-{
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- if($this->_keyList->contains($key))
- {
- $this->_keyList->remove($key);
- $this->_keyList->add($key);
- return $this->_cache->itemAt($key);
- }
- }
-
- /**
- * Stores a value identified by a key into cache.
- * The expire and dependency parameters are ignored.
- * @param string the key identifying the value to be cached
- * @param mixed the value to be cached
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->_cache->add($key, $value);
- $this->_keyList->add($key);
- if($this->_keyList->getCount() > $this->_cacheSize)
- {
- $oldestKey = $this->_keyList->removeAt(0);
- $this->_cache->remove($oldestKey);
- }
- }
-}
-
-/**
- * TSqlMapApplicationCache uses the default Prado application cache for
- * caching SqlMap results.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapApplicationCache implements ICache
-{
- protected $_cacheModel=null;
-
- /**
- * Create a new cache with limited cache size.
- * @param TSqlMapCacheModel $cacheModel.
- */
- public function __construct($cacheModel=null)
- {
- $this->_cacheModel=$cacheModel;
- }
-
- /**
- *
- * @return string a KeyListID for the cache model.
- */
- protected function getKeyListId()
- {
- $id='keyList';
- if ($this->_cacheModel instanceof TSqlMapCacheModel)
- $id.='_'.$this->_cacheModel->getId();
- return $id;
- }
- /**
- * Retreive keylist from cache or create it if it doesn't exists
- * @return TList
- */
- protected function getKeyList()
- {
- if (($keyList=$this->getCache()->get($this->getKeyListId()))===false)
- {
- $keyList=new TList();
- $this->getCache()->set($this->getKeyListId(), $keyList);
- }
- return $keyList;
- }
-
- protected function setKeyList($keyList)
- {
- $this->getCache()->set($this->getKeyListId(), $keyList);
- }
-
- /**
- * @param string item to be deleted.
- */
- public function delete($key)
- {
- $keyList=$this->getKeyList();
- $keyList->remove($key);
- $this->getCache()->delete($key);
- $this->setKeyList($keyList);
- }
-
- /**
- * Deletes all items in the cache, only for data cached by sqlmap cachemodel
- */
- public function flush()
- {
- $keyList=$this->getKeyList();
- $cache=$this->getCache();
- foreach ($keyList as $key)
- {
- $cache->delete($key);
- }
- // Remove the old keylist
- $cache->delete($this->getKeyListId());
- }
-
- /**
- * @return mixed Gets a cached object with the specified key.
- */
- public function get($key)
- {
- $result = $this->getCache()->get($key);
- if ($result === false)
- {
- // if the key has not been found in cache (e.g expired), remove from keylist
- $keyList=$this->getKeyList();
- if ($keyList->contains($key))
- {
- $keyList->remove($key);
- $this->setKeyList($keyList);
- }
- }
- return $result === false ? null : $result;
- }
-
- /**
- * Stores a value identified by a key into cache.
- * @param string the key identifying the value to be cached
- * @param mixed the value to be cached
- */
- public function set($key, $value,$expire=0,$dependency=null)
- {
- $this->getCache()->set($key, $value, $expire,$dependency);
- $keyList=$this->getKeyList();
- if (!$keyList->contains($key))
- {
- $keyList->add($key);
- $this->setKeyList($keyList);
- }
- }
-
- /**
- * @return ICache Application cache instance.
- */
- protected function getCache()
- {
- return Prado::getApplication()->getCache();
- }
-
- /**
- * @throws TSqlMapException not implemented.
- */
- public function add($id,$value,$expire=0,$dependency=null)
- {
- throw new TSqlMapException('sqlmap_use_set_to_store_cache');
- }
-}
-
+
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright © 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap
+ */
+
+/**
+ * Allow different implementation of caching strategy. See TSqlMapFifoCache
+ * for a first-in-first-out implementation. See TSqlMapLruCache for
+ * a least-recently-used cache implementation.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+abstract class TSqlMapCache implements ICache
+{
+ protected $_keyList;
+ protected $_cache;
+ protected $_cacheSize = 100;
+ protected $_cacheModel = null;
+
+ /**
+ * Create a new cache with limited cache size.
+ * @param TSqlMapCacheModel $cacheModel.
+ */
+ public function __construct($cacheModel=null)
+ {
+ $this->_cache = new TMap;
+ $this->_keyList = new TList;
+ $this->_cacheModel=$cacheModel;
+ }
+
+ /**
+ * Maximum number of items to cache. Default size is 100.
+ * @param int cache size.
+ */
+ public function setCacheSize($value)
+ {
+ $this->_cacheSize=TPropertyValue::ensureInteger($value,100);
+ }
+
+ /**
+ * @return int cache size.
+ */
+ public function getCacheSize()
+ {
+ return $this->_cacheSize;
+ }
+
+ /**
+ * @return object the object removed if exists, null otherwise.
+ */
+ public function delete($key)
+ {
+ $object = $this->get($key);
+ $this->_cache->remove($key);
+ $this->_keyList->remove($key);
+ return $object;
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public function flush()
+ {
+ $this->_keyList->clear();
+ $this->_cache->clear();
+ }
+
+ /**
+ * @throws TSqlMapException not implemented.
+ */
+ public function add($id,$value,$expire=0,$dependency=null)
+ {
+ throw new TSqlMapException('sqlmap_use_set_to_store_cache');
+ }
+}
+
+/**
+ * First-in-First-out cache implementation, removes
+ * object that was first added when the cache is full.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapFifoCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ return $this->_cache->itemAt($key);
+ }
+
+ /**
+ * Stores a value identified by a key into cache.
+ * The expire and dependency parameters are ignored.
+ * @param string cache key
+ * @param mixed value to cache.
+ */
+ public function set($key, $value,$expire=0,$dependency=null)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+/**
+ * Least recently used cache implementation, removes
+ * object that was accessed last when the cache is full.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapLruCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ if($this->_keyList->contains($key))
+ {
+ $this->_keyList->remove($key);
+ $this->_keyList->add($key);
+ return $this->_cache->itemAt($key);
+ }
+ }
+
+ /**
+ * Stores a value identified by a key into cache.
+ * The expire and dependency parameters are ignored.
+ * @param string the key identifying the value to be cached
+ * @param mixed the value to be cached
+ */
+ public function set($key, $value,$expire=0,$dependency=null)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+/**
+ * TSqlMapApplicationCache uses the default Prado application cache for
+ * caching SqlMap results.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapApplicationCache implements ICache
+{
+ protected $_cacheModel=null;
+
+ /**
+ * Create a new cache with limited cache size.
+ * @param TSqlMapCacheModel $cacheModel.
+ */
+ public function __construct($cacheModel=null)
+ {
+ $this->_cacheModel=$cacheModel;
+ }
+
+ /**
+ *
+ * @return string a KeyListID for the cache model.
+ */
+ protected function getKeyListId()
+ {
+ $id='keyList';
+ if ($this->_cacheModel instanceof TSqlMapCacheModel)
+ $id.='_'.$this->_cacheModel->getId();
+ return $id;
+ }
+ /**
+ * Retreive keylist from cache or create it if it doesn't exists
+ * @return TList
+ */
+ protected function getKeyList()
+ {
+ if (($keyList=$this->getCache()->get($this->getKeyListId()))===false)
+ {
+ $keyList=new TList();
+ $this->getCache()->set($this->getKeyListId(), $keyList);
+ }
+ return $keyList;
+ }
+
+ protected function setKeyList($keyList)
+ {
+ $this->getCache()->set($this->getKeyListId(), $keyList);
+ }
+
+ /**
+ * @param string item to be deleted.
+ */
+ public function delete($key)
+ {
+ $keyList=$this->getKeyList();
+ $keyList->remove($key);
+ $this->getCache()->delete($key);
+ $this->setKeyList($keyList);
+ }
+
+ /**
+ * Deletes all items in the cache, only for data cached by sqlmap cachemodel
+ */
+ public function flush()
+ {
+ $keyList=$this->getKeyList();
+ $cache=$this->getCache();
+ foreach ($keyList as $key)
+ {
+ $cache->delete($key);
+ }
+ // Remove the old keylist
+ $cache->delete($this->getKeyListId());
+ }
+
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ $result = $this->getCache()->get($key);
+ if ($result === false)
+ {
+ // if the key has not been found in cache (e.g expired), remove from keylist
+ $keyList=$this->getKeyList();
+ if ($keyList->contains($key))
+ {
+ $keyList->remove($key);
+ $this->setKeyList($keyList);
+ }
+ }
+ return $result === false ? null : $result;
+ }
+
+ /**
+ * Stores a value identified by a key into cache.
+ * @param string the key identifying the value to be cached
+ * @param mixed the value to be cached
+ */
+ public function set($key, $value,$expire=0,$dependency=null)
+ {
+ $this->getCache()->set($key, $value, $expire,$dependency);
+ $keyList=$this->getKeyList();
+ if (!$keyList->contains($key))
+ {
+ $keyList->add($key);
+ $this->setKeyList($keyList);
+ }
+ }
+
+ /**
+ * @return ICache Application cache instance.
+ */
+ protected function getCache()
+ {
+ return Prado::getApplication()->getCache();
+ }
+
+ /**
+ * @throws TSqlMapException not implemented.
+ */
+ public function add($id,$value,$expire=0,$dependency=null)
+ {
+ throw new TSqlMapException('sqlmap_use_set_to_store_cache');
+ }
+}
+
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapException.php b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
index 0bf0ac32..694774d8 100644
--- a/framework/Data/SqlMap/DataMapper/TSqlMapException.php
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
@@ -1,115 +1,115 @@
-
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapException extends TException
-{
- /**
- * Constructor, similar to the parent constructor. For parameters that
- * are of SimpleXmlElement, the tag name and its attribute names and values
- * are expanded into a string.
- */
- public function __construct($errorMessage)
- {
- $this->setErrorCode($errorMessage);
- $errorMessage=$this->translateErrorMessage($errorMessage);
- $args=func_get_args();
- array_shift($args);
- $n=count($args);
- $tokens=array();
- for($i=0;$i<$n;++$i)
- {
- if($args[$i] instanceof SimpleXmlElement)
- $tokens['{'.$i.'}']=$this->implodeNode($args[$i]);
- else
- $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]);
- }
- parent::__construct(strtr($errorMessage,$tokens));
- }
-
- /**
- * @param SimpleXmlElement node
- * @return string tag name and attribute names and values.
- */
- protected function implodeNode($node)
- {
- $attributes=array();
- foreach($node->attributes() as $k=>$v)
- $attributes[]=$k.'="'.(string)$v.'"';
- return '<'.$node->getName().' '.implode(' ',$attributes).'>';
- }
-
- /**
- * @return string path to the error message file
- */
- protected function getErrorMessageFile()
- {
- $lang=Prado::getPreferredLanguage();
- $dir=dirname(__FILE__);
- $msgFile=$dir.'/messages-'.$lang.'.txt';
- if(!is_file($msgFile))
- $msgFile=$dir.'/messages.txt';
- return $msgFile;
- }
-}
-
-/**
- * TSqlMapConfigurationException, raised during configuration file parsing.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapConfigurationException extends TSqlMapException
-{
-
-}
-
-/**
- * TSqlMapUndefinedException, raised when mapped statemented are undefined.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapUndefinedException extends TSqlMapException
-{
-
-}
-
-/**
- * TSqlMapDuplicateException, raised when a duplicate mapped statement is found.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TSqlMapDuplicateException extends TSqlMapException
-{
-}
-
-/**
- * TInvalidPropertyException, raised when setting or getting an invalid property.
- *
- * @author Wei Zhuo
- * @version $Id$
- * @package System.Data.SqlMap
- * @since 3.1
- */
-class TInvalidPropertyException extends TSqlMapException
-{
-}
-
-class TSqlMapExecutionException extends TSqlMapException
-{
-}
-
+
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapException extends TException
+{
+ /**
+ * Constructor, similar to the parent constructor. For parameters that
+ * are of SimpleXmlElement, the tag name and its attribute names and values
+ * are expanded into a string.
+ */
+ public function __construct($errorMessage)
+ {
+ $this->setErrorCode($errorMessage);
+ $errorMessage=$this->translateErrorMessage($errorMessage);
+ $args=func_get_args();
+ array_shift($args);
+ $n=count($args);
+ $tokens=array();
+ for($i=0;$i<$n;++$i)
+ {
+ if($args[$i] instanceof SimpleXmlElement)
+ $tokens['{'.$i.'}']=$this->implodeNode($args[$i]);
+ else
+ $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]);
+ }
+ parent::__construct(strtr($errorMessage,$tokens));
+ }
+
+ /**
+ * @param SimpleXmlElement node
+ * @return string tag name and attribute names and values.
+ */
+ protected function implodeNode($node)
+ {
+ $attributes=array();
+ foreach($node->attributes() as $k=>$v)
+ $attributes[]=$k.'="'.(string)$v.'"';
+ return '<'.$node->getName().' '.implode(' ',$attributes).'>';
+ }
+
+ /**
+ * @return string path to the error message file
+ */
+ protected function getErrorMessageFile()
+ {
+ $lang=Prado::getPreferredLanguage();
+ $dir=dirname(__FILE__);
+ $msgFile=$dir.'/messages-'.$lang.'.txt';
+ if(!is_file($msgFile))
+ $msgFile=$dir.'/messages.txt';
+ return $msgFile;
+ }
+}
+
+/**
+ * TSqlMapConfigurationException, raised during configuration file parsing.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapConfigurationException extends TSqlMapException
+{
+
+}
+
+/**
+ * TSqlMapUndefinedException, raised when mapped statemented are undefined.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapUndefinedException extends TSqlMapException
+{
+
+}
+
+/**
+ * TSqlMapDuplicateException, raised when a duplicate mapped statement is found.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TSqlMapDuplicateException extends TSqlMapException
+{
+}
+
+/**
+ * TInvalidPropertyException, raised when setting or getting an invalid property.
+ *
+ * @author Wei Zhuo
+ * @version $Id$
+ * @package System.Data.SqlMap
+ * @since 3.1
+ */
+class TInvalidPropertyException extends TSqlMapException
+{
+}
+
+class TSqlMapExecutionException extends TSqlMapException
+{
+}
+
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
index 7c6a8e87..57949561 100644
--- a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
@@ -1,208 +1,208 @@
-
- * @link http://www.pradosoft.com/
- * @copyright Copyright © 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.SqlMap
- */
-
-Prado::using('System.Collections.TPagedList');
-
-/**
- * TSqlMapPagedList implements a list with paging functionality that retrieves
- * data from a SqlMap statement.
- *
- * The maximum number of records fetched is 3 times the page size. It fetches
- * the current, the previous and the next page at a time. This allows the paged
- * list to determine if the page is a the begin, the middle or the end of the list.
- *
- * The paged list does not need to know about the total number of records.
- *
- * @author Wei Zhuo