diff --git a/autoload.php b/autoload.php index 1a1b3e2340..cfb472c597 100644 --- a/autoload.php +++ b/autoload.php @@ -1,361 +1,359 @@ __DIR__ . '/includes/api/ApiFlow.php', 'ApiFlowBase' => __DIR__ . '/includes/api/ApiFlowBase.php', 'ApiFlowBaseGet' => __DIR__ . '/includes/api/ApiFlowBaseGet.php', 'ApiFlowBasePost' => __DIR__ . '/includes/api/ApiFlowBasePost.php', 'ApiFlowEditHeader' => __DIR__ . '/includes/api/ApiFlowEditHeader.php', 'ApiFlowEditPost' => __DIR__ . '/includes/api/ApiFlowEditPost.php', 'ApiFlowEditTitle' => __DIR__ . '/includes/api/ApiFlowEditTitle.php', 'ApiFlowEditTopicSummary' => __DIR__ . '/includes/api/ApiFlowEditTopicSummary.php', 'ApiFlowLockTopic' => __DIR__ . '/includes/api/ApiFlowLockTopic.php', 'ApiFlowModeratePost' => __DIR__ . '/includes/api/ApiFlowModeratePost.php', 'ApiFlowModerateTopic' => __DIR__ . '/includes/api/ApiFlowModerateTopic.php', 'ApiFlowNewTopic' => __DIR__ . '/includes/api/ApiFlowNewTopic.php', 'ApiFlowReply' => __DIR__ . '/includes/api/ApiFlowReply.php', 'ApiFlowViewHeader' => __DIR__ . '/includes/api/ApiFlowViewHeader.php', 'ApiFlowViewPost' => __DIR__ . '/includes/api/ApiFlowViewPost.php', 'ApiFlowViewTopic' => __DIR__ . '/includes/api/ApiFlowViewTopic.php', 'ApiFlowViewTopicList' => __DIR__ . '/includes/api/ApiFlowViewTopicList.php', 'ApiFlowViewTopicSummary' => __DIR__ . '/includes/api/ApiFlowViewTopicSummary.php', 'ApiParsoidUtilsFlow' => __DIR__ . '/includes/api/ApiParsoidUtilsFlow.php', 'ApiQueryPropFlowInfo' => __DIR__ . '/includes/api/ApiQueryPropFlowInfo.php', 'FlowHooks' => __DIR__ . '/Hooks.php', 'Flow\\Actions\\CompareHeaderRevisionsAction' => __DIR__ . '/includes/Actions/CompareHeaderRevisionsAction.php', 'Flow\\Actions\\ComparePostRevisionsAction' => __DIR__ . '/includes/Actions/ComparePostRevisionsAction.php', 'Flow\\Actions\\ComparePostSummaryRevisionsAction' => __DIR__ . '/includes/Actions/ComparePostSummaryRevisionsAction.php', 'Flow\\Actions\\CreateTopicSummaryAction' => __DIR__ . '/includes/Actions/CreateTopicSummaryAction.php', 'Flow\\Actions\\EditAction' => __DIR__ . '/includes/Actions/EditAction.php', 'Flow\\Actions\\EditHeaderAction' => __DIR__ . '/includes/Actions/EditHeaderAction.php', 'Flow\\Actions\\EditPostAction' => __DIR__ . '/includes/Actions/EditPostAction.php', 'Flow\\Actions\\EditTitleAction' => __DIR__ . '/includes/Actions/EditTitleAction.php', 'Flow\\Actions\\EditTopicSummaryAction' => __DIR__ . '/includes/Actions/EditTopicSummaryAction.php', 'Flow\\Actions\\FlowAction' => __DIR__ . '/includes/Actions/Action.php', 'Flow\\Actions\\HistoryAction' => __DIR__ . '/includes/Actions/HistoryAction.php', 'Flow\\Actions\\LockTopicAction' => __DIR__ . '/includes/Actions/LockTopicAction.php', 'Flow\\Actions\\ModeratePostAction' => __DIR__ . '/includes/Actions/ModeratePostAction.php', 'Flow\\Actions\\ModerateTopicAction' => __DIR__ . '/includes/Actions/ModerateTopicAction.php', 'Flow\\Actions\\NewTopicAction' => __DIR__ . '/includes/Actions/NewTopicAction.php', 'Flow\\Actions\\PostSingleViewAction' => __DIR__ . '/includes/Actions/PostSingleViewAction.php', 'Flow\\Actions\\PurgeAction' => __DIR__ . '/includes/Actions/PurgeAction.php', 'Flow\\Actions\\ReplyAction' => __DIR__ . '/includes/Actions/ReplyAction.php', 'Flow\\Actions\\RestorePostAction' => __DIR__ . '/includes/Actions/RestorePostAction.php', 'Flow\\Actions\\RestoreTopicAction' => __DIR__ . '/includes/Actions/RestoreTopicAction.php', 'Flow\\Actions\\ViewAction' => __DIR__ . '/includes/Actions/ViewAction.php', 'Flow\\Actions\\ViewHeaderAction' => __DIR__ . '/includes/Actions/ViewHeaderAction.php', 'Flow\\Actions\\ViewTopicSummaryAction' => __DIR__ . '/includes/Actions/ViewTopicSummaryAction.php', 'Flow\\BlockFactory' => __DIR__ . '/includes/BlockFactory.php', 'Flow\\Block\\AbstractBlock' => __DIR__ . '/includes/Block/Block.php', 'Flow\\Block\\Block' => __DIR__ . '/includes/Block/Block.php', 'Flow\\Block\\BoardHistoryBlock' => __DIR__ . '/includes/Block/BoardHistory.php', 'Flow\\Block\\HeaderBlock' => __DIR__ . '/includes/Block/Header.php', 'Flow\\Block\\TopicBlock' => __DIR__ . '/includes/Block/Topic.php', 'Flow\\Block\\TopicListBlock' => __DIR__ . '/includes/Block/TopicList.php', 'Flow\\Block\\TopicSummaryBlock' => __DIR__ . '/includes/Block/TopicSummary.php', 'Flow\\Collection\\AbstractCollection' => __DIR__ . '/includes/Collection/AbstractCollection.php', 'Flow\\Collection\\CollectionCache' => __DIR__ . '/includes/Collection/CollectionCache.php', 'Flow\\Collection\\HeaderCollection' => __DIR__ . '/includes/Collection/HeaderCollection.php', 'Flow\\Collection\\LocalCacheAbstractCollection' => __DIR__ . '/includes/Collection/LocalCacheAbstractCollection.php', 'Flow\\Collection\\PostCollection' => __DIR__ . '/includes/Collection/PostCollection.php', 'Flow\\Collection\\PostSummaryCollection' => __DIR__ . '/includes/Collection/PostSummaryCollection.php', 'Flow\\Container' => __DIR__ . '/includes/Container.php', 'Flow\\Content\\BoardContent' => __DIR__ . '/includes/Content/BoardContent.php', 'Flow\\Content\\BoardContentHandler' => __DIR__ . '/includes/Content/BoardContentHandler.php', 'Flow\\Content\\Content' => __DIR__ . '/includes/Content/Content.php', 'Flow\\Data\\BagOStuff\\BufferedBagOStuff' => __DIR__ . '/includes/Data/BagOStuff/BufferedBagOStuff.php', 'Flow\\Data\\BagOStuff\\LocalBufferedBagOStuff' => __DIR__ . '/includes/Data/BagOStuff/LocalBufferedBagOStuff.php', 'Flow\\Data\\BufferedCache' => __DIR__ . '/includes/Data/BufferedCache.php', 'Flow\\Data\\Compactor' => __DIR__ . '/includes/Data/Compactor.php', 'Flow\\Data\\Compactor\\FeatureCompactor' => __DIR__ . '/includes/Data/Compactor/FeatureCompactor.php', 'Flow\\Data\\Compactor\\ShallowCompactor' => __DIR__ . '/includes/Data/Compactor/ShallowCompactor.php', 'Flow\\Data\\Index' => __DIR__ . '/includes/Data/Index.php', 'Flow\\Data\\Index\\BoardHistoryIndex' => __DIR__ . '/includes/Data/Index/BoardHistoryIndex.php', 'Flow\\Data\\Index\\FeatureIndex' => __DIR__ . '/includes/Data/Index/FeatureIndex.php', 'Flow\\Data\\Index\\TopKIndex' => __DIR__ . '/includes/Data/Index/TopKIndex.php', 'Flow\\Data\\Index\\TopicHistoryIndex' => __DIR__ . '/includes/Data/Index/TopicHistoryIndex.php', 'Flow\\Data\\Index\\UniqueFeatureIndex' => __DIR__ . '/includes/Data/Index/UniqueFeatureIndex.php', 'Flow\\Data\\LifecycleHandler' => __DIR__ . '/includes/Data/LifecycleHandler.php', 'Flow\\Data\\Listener\\AbstractTopicInsertListener' => __DIR__ . '/includes/Data/Listener/WatchTopicListener.php', 'Flow\\Data\\Listener\\DeferredInsertLifecycleHandler' => __DIR__ . '/includes/Data/Listener/DeferredInsertLifecycleHandler.php', 'Flow\\Data\\Listener\\ImmediateWatchTopicListener' => __DIR__ . '/includes/Data/Listener/WatchTopicListener.php', 'Flow\\Data\\Listener\\NotificationListener' => __DIR__ . '/includes/Data/Listener/NotificationListener.php', 'Flow\\Data\\Listener\\OccupationListener' => __DIR__ . '/includes/Data/Listener/OccupationListener.php', 'Flow\\Data\\Listener\\RecentChangesListener' => __DIR__ . '/includes/Data/Listener/RecentChangesListener.php', 'Flow\\Data\\Listener\\ReferenceRecorder' => __DIR__ . '/includes/Data/Listener/ReferenceRecorder.php', 'Flow\\Data\\Listener\\UrlGenerationListener' => __DIR__ . '/includes/Data/Listener/UrlGenerationListener.php', 'Flow\\Data\\Listener\\UserNameListener' => __DIR__ . '/includes/Data/Listener/UserNameListener.php', 'Flow\\Data\\Listener\\WorkflowTopicListListener' => __DIR__ . '/includes/Data/Listener/WorkflowTopicListListener.php', 'Flow\\Data\\ManagerGroup' => __DIR__ . '/includes/Data/ManagerGroup.php', 'Flow\\Data\\Mapper\\BasicObjectMapper' => __DIR__ . '/includes/Data/Mapper/BasicObjectMapper.php', 'Flow\\Data\\Mapper\\CachingObjectMapper' => __DIR__ . '/includes/Data/Mapper/CachingObjectMapper.php', 'Flow\\Data\\ObjectLocator' => __DIR__ . '/includes/Data/ObjectLocator.php', 'Flow\\Data\\ObjectManager' => __DIR__ . '/includes/Data/ObjectManager.php', 'Flow\\Data\\ObjectMapper' => __DIR__ . '/includes/Data/ObjectMapper.php', 'Flow\\Data\\ObjectStorage' => __DIR__ . '/includes/Data/ObjectStorage.php', 'Flow\\Data\\Pager\\Pager' => __DIR__ . '/includes/Data/Pager/Pager.php', 'Flow\\Data\\Pager\\PagerPage' => __DIR__ . '/includes/Data/Pager/PagerPage.php', 'Flow\\Data\\Storage\\BasicDbStorage' => __DIR__ . '/includes/Data/Storage/BasicDbStorage.php', 'Flow\\Data\\Storage\\BoardHistoryStorage' => __DIR__ . '/includes/Data/Storage/BoardHistoryStorage.php', 'Flow\\Data\\Storage\\DbStorage' => __DIR__ . '/includes/Data/Storage/DbStorage.php', 'Flow\\Data\\Storage\\HeaderRevisionStorage' => __DIR__ . '/includes/Data/Storage/HeaderRevisionStorage.php', 'Flow\\Data\\Storage\\PostRevisionStorage' => __DIR__ . '/includes/Data/Storage/PostRevisionStorage.php', 'Flow\\Data\\Storage\\PostSummaryRevisionStorage' => __DIR__ . '/includes/Data/Storage/PostSummaryRevisionStorage.php', 'Flow\\Data\\Storage\\RevisionStorage' => __DIR__ . '/includes/Data/Storage/RevisionStorage.php', 'Flow\\Data\\Storage\\TopicHistoryStorage' => __DIR__ . '/includes/Data/Storage/TopicHistoryStorage.php', 'Flow\\Data\\Storage\\TopicListLastUpdatedStorage' => __DIR__ . '/includes/Data/Storage/TopicListLastUpdatedStorage.php', 'Flow\\Data\\Storage\\TopicListStorage' => __DIR__ . '/includes/Data/Storage/TopicListStorage.php', 'Flow\\Data\\Utils\\Merger' => __DIR__ . '/includes/Data/Utils/Merger.php', 'Flow\\Data\\Utils\\MultiDimArray' => __DIR__ . '/includes/Data/Utils/MultiDimArray.php', 'Flow\\Data\\Utils\\RawSql' => __DIR__ . '/includes/Data/Utils/RawSql.php', 'Flow\\Data\\Utils\\RecentChangeFactory' => __DIR__ . '/includes/Data/Utils/RecentChangeFactory.php', 'Flow\\Data\\Utils\\ResultDuplicator' => __DIR__ . '/includes/Data/Utils/ResultDuplicator.php', 'Flow\\Data\\Utils\\SortArrayByKeys' => __DIR__ . '/includes/Data/Utils/SortArrayByKeys.php', 'Flow\\Data\\Utils\\UserMerger' => __DIR__ . '/includes/Data/Utils/UserMerger.php', 'Flow\\DbFactory' => __DIR__ . '/includes/DbFactory.php', 'Flow\\Exception\\CatchableFatalErrorException' => __DIR__ . '/includes/Exception/CatchableFatalErrorException.php', 'Flow\\Exception\\CrossWikiException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\DataModelException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\DataPersistenceException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\FailCommitException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\FlowException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\InvalidActionException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\InvalidDataException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\InvalidInputException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\InvalidTopicUuidException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\NoIndexException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\NoParsoidException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\PermissionException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\UnknownWorkflowIdException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\WikitextException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\Exception\\WrongNumberArgumentsException' => __DIR__ . '/includes/Exception/ExceptionHandling.php', 'Flow\\FlowActions' => __DIR__ . '/includes/FlowActions.php', 'Flow\\Formatter\\AbstractFormatter' => __DIR__ . '/includes/Formatter/AbstractFormatter.php', 'Flow\\Formatter\\AbstractQuery' => __DIR__ . '/includes/Formatter/AbstractQuery.php', 'Flow\\Formatter\\BaseTopicListFormatter' => __DIR__ . '/includes/Formatter/BaseTopicListFormatter.php', 'Flow\\Formatter\\BoardHistoryQuery' => __DIR__ . '/includes/Formatter/BoardHistoryQuery.php', 'Flow\\Formatter\\CheckUserFormatter' => __DIR__ . '/includes/Formatter/CheckUserFormatter.php', 'Flow\\Formatter\\CheckUserQuery' => __DIR__ . '/includes/Formatter/CheckUserQuery.php', 'Flow\\Formatter\\CheckUserRow' => __DIR__ . '/includes/Formatter/CheckUserQuery.php', 'Flow\\Formatter\\Contributions' => __DIR__ . '/includes/Formatter/Contributions.php', 'Flow\\Formatter\\ContributionsQuery' => __DIR__ . '/includes/Formatter/ContributionsQuery.php', 'Flow\\Formatter\\ContributionsRow' => __DIR__ . '/includes/Formatter/ContributionsQuery.php', 'Flow\\Formatter\\FeedItemFormatter' => __DIR__ . '/includes/Formatter/FeedItemFormatter.php', 'Flow\\Formatter\\FormatterRow' => __DIR__ . '/includes/Formatter/AbstractQuery.php', 'Flow\\Formatter\\HeaderViewQuery' => __DIR__ . '/includes/Formatter/RevisionViewQuery.php', 'Flow\\Formatter\\IRCLineUrlFormatter' => __DIR__ . '/includes/Formatter/IRCLineUrlFormatter.php', 'Flow\\Formatter\\PostHistoryQuery' => __DIR__ . '/includes/Formatter/PostHistoryQuery.php', 'Flow\\Formatter\\PostSummaryQuery' => __DIR__ . '/includes/Formatter/PostSummaryQuery.php', 'Flow\\Formatter\\PostSummaryViewQuery' => __DIR__ . '/includes/Formatter/RevisionViewQuery.php', 'Flow\\Formatter\\PostViewQuery' => __DIR__ . '/includes/Formatter/RevisionViewQuery.php', 'Flow\\Formatter\\RecentChanges' => __DIR__ . '/includes/Formatter/RecentChanges.php', 'Flow\\Formatter\\RecentChangesQuery' => __DIR__ . '/includes/Formatter/RecentChangesQuery.php', 'Flow\\Formatter\\RecentChangesRow' => __DIR__ . '/includes/Formatter/RecentChangesQuery.php', 'Flow\\Formatter\\RevisionDiffViewFormatter' => __DIR__ . '/includes/Formatter/RevisionViewFormatter.php', 'Flow\\Formatter\\RevisionFormatter' => __DIR__ . '/includes/Formatter/RevisionFormatter.php', 'Flow\\Formatter\\RevisionViewFormatter' => __DIR__ . '/includes/Formatter/RevisionViewFormatter.php', 'Flow\\Formatter\\RevisionViewQuery' => __DIR__ . '/includes/Formatter/RevisionViewQuery.php', 'Flow\\Formatter\\SinglePostQuery' => __DIR__ . '/includes/Formatter/SinglePostQuery.php', 'Flow\\Formatter\\TocTopicListFormatter' => __DIR__ . '/includes/Formatter/TocTopicListFormatter.php', 'Flow\\Formatter\\TopicFormatter' => __DIR__ . '/includes/Formatter/TopicFormatter.php', 'Flow\\Formatter\\TopicHistoryQuery' => __DIR__ . '/includes/Formatter/TopicHistoryQuery.php', 'Flow\\Formatter\\TopicListFormatter' => __DIR__ . '/includes/Formatter/TopicListFormatter.php', 'Flow\\Formatter\\TopicListQuery' => __DIR__ . '/includes/Formatter/TopicListQuery.php', 'Flow\\Formatter\\TopicRow' => __DIR__ . '/includes/Formatter/TopicRow.php', 'Flow\\Import\\Converter' => __DIR__ . '/includes/Import/Converter.php', 'Flow\\Import\\FileImportSourceStore' => __DIR__ . '/includes/Import/ImportSourceStore.php', 'Flow\\Import\\HistoricalUIDGenerator' => __DIR__ . '/includes/Import/Importer.php', 'Flow\\Import\\IConversionStrategy' => __DIR__ . '/includes/Import/IConversionStrategy.php', 'Flow\\Import\\IImportHeader' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IImportObject' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IImportPost' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IImportSource' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IImportSummary' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IImportTopic' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IObjectRevision' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\IRevisionableObject' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\ImportException' => __DIR__ . '/includes/Import/ImportSource.php', 'Flow\\Import\\ImportSourceStore' => __DIR__ . '/includes/Import/ImportSourceStore.php', 'Flow\\Import\\Importer' => __DIR__ . '/includes/Import/Importer.php', 'Flow\\Import\\LiquidThreadsApi\\ApiBackend' => __DIR__ . '/includes/Import/LiquidThreadsApi/Source.php', 'Flow\\Import\\LiquidThreadsApi\\ApiNotFoundException' => __DIR__ . '/includes/Import/LiquidThreadsApi/Source.php', 'Flow\\Import\\LiquidThreadsApi\\CachedApiData' => __DIR__ . '/includes/Import/LiquidThreadsApi/CachedData.php', 'Flow\\Import\\LiquidThreadsApi\\CachedData' => __DIR__ . '/includes/Import/LiquidThreadsApi/CachedData.php', 'Flow\\Import\\LiquidThreadsApi\\CachedPageData' => __DIR__ . '/includes/Import/LiquidThreadsApi/CachedData.php', 'Flow\\Import\\LiquidThreadsApi\\CachedThreadData' => __DIR__ . '/includes/Import/LiquidThreadsApi/CachedData.php', 'Flow\\Import\\LiquidThreadsApi\\ConversionStrategy' => __DIR__ . '/includes/Import/LiquidThreadsApi/ConversionStrategy.php', 'Flow\\Import\\LiquidThreadsApi\\ImportHeader' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\ImportPost' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\ImportRevision' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\ImportSource' => __DIR__ . '/includes/Import/LiquidThreadsApi/Source.php', 'Flow\\Import\\LiquidThreadsApi\\ImportSummary' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\ImportTopic' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\LocalApiBackend' => __DIR__ . '/includes/Import/LiquidThreadsApi/Source.php', 'Flow\\Import\\LiquidThreadsApi\\PageRevisionedObject' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\RemoteApiBackend' => __DIR__ . '/includes/Import/LiquidThreadsApi/Source.php', 'Flow\\Import\\LiquidThreadsApi\\ReplyIterator' => __DIR__ . '/includes/Import/LiquidThreadsApi/Iterators.php', 'Flow\\Import\\LiquidThreadsApi\\RevisionIterator' => __DIR__ . '/includes/Import/LiquidThreadsApi/Iterators.php', 'Flow\\Import\\LiquidThreadsApi\\ScriptedImportRevision' => __DIR__ . '/includes/Import/LiquidThreadsApi/Objects.php', 'Flow\\Import\\LiquidThreadsApi\\TopicIterator' => __DIR__ . '/includes/Import/LiquidThreadsApi/Iterators.php', 'Flow\\Import\\NullImportSourceStore' => __DIR__ . '/includes/Import/ImportSourceStore.php', 'Flow\\Import\\PageImportState' => __DIR__ . '/includes/Import/Importer.php', 'Flow\\Import\\Plain\\ImportHeader' => __DIR__ . '/includes/Import/Plain/ImportHeader.php', 'Flow\\Import\\Plain\\ObjectRevision' => __DIR__ . '/includes/Import/Plain/ObjectRevision.php', 'Flow\\Import\\Postprocessor\\LqtRedirector' => __DIR__ . '/includes/Import/Postprocessor/LqtRedirector.php', 'Flow\\Import\\Postprocessor\\PostprocessingException' => __DIR__ . '/includes/Import/Postprocessor/PostprocessingException.php', 'Flow\\Import\\Postprocessor\\Postprocessor' => __DIR__ . '/includes/Import/Postprocessor/Postprocessor.php', 'Flow\\Import\\Postprocessor\\ProcessorGroup' => __DIR__ . '/includes/Import/Postprocessor/ProcessorGroup.php', 'Flow\\Import\\TalkpageImportOperation' => __DIR__ . '/includes/Import/Importer.php', 'Flow\\Import\\TopicImportState' => __DIR__ . '/includes/Import/Importer.php', 'Flow\\Import\\Wikitext\\ConversionStrategy' => __DIR__ . '/includes/Import/Wikitext/ConversionStrategy.php', 'Flow\\Import\\Wikitext\\ImportSource' => __DIR__ . '/includes/Import/Wikitext/ImportSource.php', 'Flow\\LinksTableUpdater' => __DIR__ . '/includes/LinksTableUpdater.php', 'Flow\\Log\\Formatter' => __DIR__ . '/includes/Log/Formatter.php', 'Flow\\Log\\Logger' => __DIR__ . '/includes/Log/Logger.php', 'Flow\\Log\\PostModerationLogger' => __DIR__ . '/includes/Log/PostModerationLogger.php', 'Flow\\Model\\AbstractRevision' => __DIR__ . '/includes/Model/AbstractRevision.php', 'Flow\\Model\\AbstractSummary' => __DIR__ . '/includes/Model/AbstractSummary.php', 'Flow\\Model\\Anchor' => __DIR__ . '/includes/Model/Anchor.php', 'Flow\\Model\\Header' => __DIR__ . '/includes/Model/Header.php', 'Flow\\Model\\PostRevision' => __DIR__ . '/includes/Model/PostRevision.php', 'Flow\\Model\\PostSummary' => __DIR__ . '/includes/Model/PostSummary.php', 'Flow\\Model\\Reference' => __DIR__ . '/includes/Model/Reference.php', 'Flow\\Model\\TopicListEntry' => __DIR__ . '/includes/Model/TopicListEntry.php', 'Flow\\Model\\URLReference' => __DIR__ . '/includes/Model/Reference.php', 'Flow\\Model\\UUID' => __DIR__ . '/includes/Model/UUID.php', 'Flow\\Model\\UserTuple' => __DIR__ . '/includes/Model/UserTuple.php', 'Flow\\Model\\WikiReference' => __DIR__ . '/includes/Model/Reference.php', 'Flow\\Model\\Workflow' => __DIR__ . '/includes/Model/Workflow.php', 'Flow\\NewTopicFormatter' => __DIR__ . '/includes/Notifications/Formatter.php', 'Flow\\NotificationController' => __DIR__ . '/includes/Notifications/Controller.php', 'Flow\\NotificationFormatter' => __DIR__ . '/includes/Notifications/Formatter.php', 'Flow\\NotificationsUserLocator' => __DIR__ . '/includes/Notifications/UserLocator.php', 'Flow\\OccupationController' => __DIR__ . '/includes/TalkpageManager.php', 'Flow\\Parsoid\\ContentFixer' => __DIR__ . '/includes/Parsoid/ContentFixer.php', 'Flow\\Parsoid\\Extractor' => __DIR__ . '/includes/Parsoid/Extractor.php', 'Flow\\Parsoid\\Extractor\\ExtLinkExtractor' => __DIR__ . '/includes/Parsoid/Extractor/ExtLinkExtractor.php', 'Flow\\Parsoid\\Extractor\\ImageExtractor' => __DIR__ . '/includes/Parsoid/Extractor/ImageExtractor.php', 'Flow\\Parsoid\\Extractor\\PlaceholderExtractor' => __DIR__ . '/includes/Parsoid/Extractor/PlaceholderExtractor.php', 'Flow\\Parsoid\\Extractor\\TransclusionExtractor' => __DIR__ . '/includes/Parsoid/Extractor/TransclusionExtractor.php', 'Flow\\Parsoid\\Extractor\\WikiLinkExtractor' => __DIR__ . '/includes/Parsoid/Extractor/WikiLinkExtractor.php', 'Flow\\Parsoid\\Fixer' => __DIR__ . '/includes/Parsoid/Fixer.php', 'Flow\\Parsoid\\Fixer\\BadImageRemover' => __DIR__ . '/includes/Parsoid/Fixer/BadImageRemover.php', 'Flow\\Parsoid\\Fixer\\Redlinker' => __DIR__ . '/includes/Parsoid/Fixer/Redlinker.php', 'Flow\\Parsoid\\ReferenceExtractor' => __DIR__ . '/includes/Parsoid/ReferenceExtractor.php', 'Flow\\Parsoid\\ReferenceFactory' => __DIR__ . '/includes/Parsoid/ReferenceFactory.php', 'Flow\\Parsoid\\Utils' => __DIR__ . '/includes/Parsoid/Utils.php', 'Flow\\RecoverableErrorHandler' => __DIR__ . '/includes/RecoverableErrorHandler.php', 'Flow\\ReferenceClarifier' => __DIR__ . '/includes/ReferenceClarifier.php', 'Flow\\Repository\\MultiGetList' => __DIR__ . '/includes/Repository/MultiGetList.php', 'Flow\\Repository\\RootPostLoader' => __DIR__ . '/includes/Repository/RootPostLoader.php', 'Flow\\Repository\\TitleRepository' => __DIR__ . '/includes/Repository/TitleRepository.php', 'Flow\\Repository\\TreeRepository' => __DIR__ . '/includes/Repository/TreeRepository.php', 'Flow\\Repository\\UserNameBatch' => __DIR__ . '/includes/Repository/UserNameBatch.php', 'Flow\\Repository\\UserName\\OneStepUserNameQuery' => __DIR__ . '/includes/Repository/UserName/OneStepUserNameQuery.php', 'Flow\\Repository\\UserName\\TwoStepUserNameQuery' => __DIR__ . '/includes/Repository/UserName/TwoStepUserNameQuery.php', 'Flow\\Repository\\UserName\\UserNameQuery' => __DIR__ . '/includes/Repository/UserName/UserNameQuery.php', 'Flow\\RevisionActionPermissions' => __DIR__ . '/includes/RevisionActionPermissions.php', 'Flow\\SpamFilter\\AbuseFilter' => __DIR__ . '/includes/SpamFilter/AbuseFilter.php', 'Flow\\SpamFilter\\ConfirmEdit' => __DIR__ . '/includes/SpamFilter/ConfirmEdit.php', 'Flow\\SpamFilter\\ContentLengthFilter' => __DIR__ . '/includes/SpamFilter/ContentLengthFilter.php', 'Flow\\SpamFilter\\Controller' => __DIR__ . '/includes/SpamFilter/Controller.php', 'Flow\\SpamFilter\\SpamBlacklist' => __DIR__ . '/includes/SpamFilter/SpamBlacklist.php', 'Flow\\SpamFilter\\SpamFilter' => __DIR__ . '/includes/SpamFilter/SpamFilter.php', 'Flow\\SpamFilter\\SpamRegex' => __DIR__ . '/includes/SpamFilter/SpamRegex.php', 'Flow\\SpecialFlow' => __DIR__ . '/includes/SpecialFlow.php', 'Flow\\SubmissionHandler' => __DIR__ . '/includes/SubmissionHandler.php', 'Flow\\TalkpageManager' => __DIR__ . '/includes/TalkpageManager.php', 'Flow\\TemplateHelper' => __DIR__ . '/includes/TemplateHelper.php', 'Flow\\Templating' => __DIR__ . '/includes/Templating.php', 'Flow\\Tests\\Api\\ApiFlowEditHeaderTest' => __DIR__ . '/tests/phpunit/api/ApiFlowEditHeaderTest.php', 'Flow\\Tests\\Api\\ApiFlowEditPostTest' => __DIR__ . '/tests/phpunit/api/ApiFlowEditPostTest.php', 'Flow\\Tests\\Api\\ApiFlowEditTitleTest' => __DIR__ . '/tests/phpunit/api/ApiFlowEditTitleTest.php', 'Flow\\Tests\\Api\\ApiFlowEditTopicSummaryTest' => __DIR__ . '/tests/phpunit/api/ApiFlowEditTopicSummary.php', 'Flow\\Tests\\Api\\ApiFlowLockTopicTest' => __DIR__ . '/tests/phpunit/api/ApiFlowLockTopicTest.php', 'Flow\\Tests\\Api\\ApiFlowModeratePostTest' => __DIR__ . '/tests/phpunit/api/ApiFlowModeratePostTest.php', 'Flow\\Tests\\Api\\ApiFlowModerateTopicTest' => __DIR__ . '/tests/phpunit/api/ApiFlowModerateTopicTest.php', 'Flow\\Tests\\Api\\ApiFlowReplyTest' => __DIR__ . '/tests/phpunit/api/ApiFlowReplyTest.php', 'Flow\\Tests\\Api\\ApiFlowViewHeaderTest' => __DIR__ . '/tests/phpunit/api/ApiFlowViewHeaderTest.php', 'Flow\\Tests\\Api\\ApiFlowViewTopicListTest' => __DIR__ . '/tests/phpunit/api/ApiFlowViewTopicListTest.php', 'Flow\\Tests\\Api\\ApiTestCase' => __DIR__ . '/tests/phpunit/api/ApiTestCase.php', 'Flow\\Tests\\Api\\ApiWatchTopicTest' => __DIR__ . '/tests/phpunit/api/ApiWatchTopicTest.php', 'Flow\\Tests\\BlockFactoryTest' => __DIR__ . '/tests/phpunit/BlockFactoryTest.php', 'Flow\\Tests\\Block\\TopicListTest' => __DIR__ . '/tests/phpunit/Block/TopicListTest.php', 'Flow\\Tests\\BufferedBagOStuffTest' => __DIR__ . '/tests/phpunit/Data/BagOStuff/BufferedBagOStuffTest.php', 'Flow\\Tests\\BufferedCacheTest' => __DIR__ . '/tests/phpunit/Data/BufferedCacheTest.php', 'Flow\\Tests\\Collection\\PostCollectionTest' => __DIR__ . '/tests/phpunit/Collection/PostCollectionTest.php', 'Flow\\Tests\\Collection\\RevisionCollectionPermissionsTest' => __DIR__ . '/tests/phpunit/Collection/RevisionCollectionPermissionsTest.php', 'Flow\\Tests\\ContainerTest' => __DIR__ . '/tests/phpunit/ContainerTest.php', 'Flow\\Tests\\Data\\CachingObjectManagerTest' => __DIR__ . '/tests/phpunit/Data/CachingObjectMapperTest.php', 'Flow\\Tests\\Data\\FlowNothingTest' => __DIR__ . '/tests/phpunit/Data/NothingTest.php', 'Flow\\Tests\\Data\\IndexTest' => __DIR__ . '/tests/phpunit/Data/IndexTest.php', 'Flow\\Tests\\Data\\Index\\FeatureIndexTest' => __DIR__ . '/tests/phpunit/Data/Index/FeatureIndexTest.php', 'Flow\\Tests\\Data\\Index\\MockFeatureIndex' => __DIR__ . '/tests/phpunit/Data/Index/FeatureIndexTest.php', 'Flow\\Tests\\Data\\Listener\\RecentChangesListenerTest' => __DIR__ . '/tests/phpunit/Data/Listener/RecentChangesListenerTest.php', 'Flow\\Tests\\Data\\ManagerGroupTest' => __DIR__ . '/tests/phpunit/Data/ManagerGroupTest.php', 'Flow\\Tests\\Data\\ObjectLocatorTest' => __DIR__ . '/tests/phpunit/Data/ObjectLocatorTest.php', 'Flow\\Tests\\Data\\Pager\\PagerTest' => __DIR__ . '/tests/phpunit/Data/Pager/PagerTest.php', 'Flow\\Tests\\Data\\RevisionStorageTest' => __DIR__ . '/tests/phpunit/Data/RevisionStorageTest.php', 'Flow\\Tests\\Data\\Storage\\RevisionStorageTest' => __DIR__ . '/tests/phpunit/Data/Storage/RevisionStorageTest.php', 'Flow\\Tests\\Data\\UserNameBatchTest' => __DIR__ . '/tests/phpunit/Data/UserNameBatchTest.php', 'Flow\\Tests\\Data\\UserNameListenerTest' => __DIR__ . '/tests/phpunit/Data/UserNameListenerTest.php', 'Flow\\Tests\\FlowActionsTest' => __DIR__ . '/tests/phpunit/FlowActionsTest.php', 'Flow\\Tests\\FlowTestCase' => __DIR__ . '/tests/phpunit/FlowTestCase.php', 'Flow\\Tests\\Formatter\\FormatterTest' => __DIR__ . '/tests/phpunit/Formatter/FormatterTest.php', 'Flow\\Tests\\Formatter\\RevisionFormatterTest' => __DIR__ . '/tests/phpunit/Formatter/RevisionFormatterTest.php', 'Flow\\Tests\\Handlebars\\FlowPostMetaActionsTest' => __DIR__ . '/tests/phpunit/Handlebars/FlowPostMetaActionsTest.php', 'Flow\\Tests\\HookTest' => __DIR__ . '/tests/phpunit/HookTest.php', 'Flow\\Tests\\Import\\ConverterTest' => __DIR__ . '/tests/phpunit/Import/ConverterTest.php', 'Flow\\Tests\\Import\\HistoricalUIDGeneratorTest' => __DIR__ . '/tests/phpunit/Import/HistoricalUIDGeneratorTest.php', 'Flow\\Tests\\Import\\LiquidThreadsApi\\ConversionStrategyTest' => __DIR__ . '/tests/phpunit/Import/LiquidThreadsApi/ConversionStrategyTest.php', 'Flow\\Tests\\Import\\PageImportStateTest' => __DIR__ . '/tests/phpunit/Import/PageImportStateTest.php', 'Flow\\Tests\\Import\\TalkpageImportOperationTest' => __DIR__ . '/tests/phpunit/Import/TalkpageImportOperationTest.php', 'Flow\\Tests\\Import\\Wikitext\\ConversionStrategyTest' => __DIR__ . '/tests/phpunit/Import/Wikitext/ConversionStrategyTest.php', 'Flow\\Tests\\Import\\Wikitext\\ImportSourceTest' => __DIR__ . '/tests/phpunit/Import/Wikitext/ImportSourceTest.php', 'Flow\\Tests\\LinksTableTest' => __DIR__ . '/tests/phpunit/LinksTableTest.php', 'Flow\\Tests\\LocalBufferedBagOStuffTest' => __DIR__ . '/tests/phpunit/Data/BagOStuff/LocalBufferedBagOStuffTest.php', 'Flow\\Tests\\Mock\\MockImportHeader' => __DIR__ . '/tests/phpunit/Mock/MockImportHeader.php', 'Flow\\Tests\\Mock\\MockImportPost' => __DIR__ . '/tests/phpunit/Mock/MockImportPost.php', 'Flow\\Tests\\Mock\\MockImportRevision' => __DIR__ . '/tests/phpunit/Mock/MockImportRevision.php', 'Flow\\Tests\\Mock\\MockImportSource' => __DIR__ . '/tests/phpunit/Mock/MockImportSource.php', 'Flow\\Tests\\Mock\\MockImportSummary' => __DIR__ . '/tests/phpunit/Mock/MockImportSummary.php', 'Flow\\Tests\\Mock\\MockImportTopic' => __DIR__ . '/tests/phpunit/Mock/MockImportTopic.php', 'Flow\\Tests\\Model\\PostRevisionTest' => __DIR__ . '/tests/phpunit/Model/PostRevisionTest.php', 'Flow\\Tests\\Model\\UUIDTest' => __DIR__ . '/tests/phpunit/Model/UUIDTest.php', 'Flow\\Tests\\Model\\UserTupleTest' => __DIR__ . '/tests/phpunit/Model/UserTupleTest.php', 'Flow\\Tests\\NotifiedUsersTest' => __DIR__ . '/tests/phpunit/Notifications/NotifiedUsersTest.php', 'Flow\\Tests\\PagerTest' => __DIR__ . '/tests/phpunit/PagerTest.php', 'Flow\\Tests\\Parsoid\\BadImageRemoverTest' => __DIR__ . '/tests/phpunit/Parsoid/Fixer/BadImageRemoverTest.php', 'Flow\\Tests\\Parsoid\\Fixer\\MethodReturnsConstraint' => __DIR__ . '/tests/phpunit/Parsoid/Fixer/RedlinkerTest.php', 'Flow\\Tests\\Parsoid\\Fixer\\RedlinkerTest' => __DIR__ . '/tests/phpunit/Parsoid/Fixer/RedlinkerTest.php', 'Flow\\Tests\\Parsoid\\ParsoidUtilsTest' => __DIR__ . '/tests/phpunit/Parsoid/UtilsTest.php', 'Flow\\Tests\\Parsoid\\ReferenceExtractorTestCase' => __DIR__ . '/tests/phpunit/Parsoid/ReferenceExtractorTest.php', 'Flow\\Tests\\Parsoid\\ReferenceFactoryTest' => __DIR__ . '/tests/phpunit/Parsoid/ReferenceFactoryTest.php', 'Flow\\Tests\\PermissionsTest' => __DIR__ . '/tests/phpunit/PermissionsTest.php', 'Flow\\Tests\\PostRevisionTestCase' => __DIR__ . '/tests/phpunit/PostRevisionTestCase.php', 'Flow\\Tests\\Repository\\TreeRepositoryTest' => __DIR__ . '/tests/phpunit/Repository/TreeRepositoryTest.php', 'Flow\\Tests\\Repository\\TreeRepositorydbTest' => __DIR__ . '/tests/phpunit/Repository/TreeRepositoryDbTest.php', 'Flow\\Tests\\SpamFilter\\AbuseFilterTest' => __DIR__ . '/tests/phpunit/SpamFilter/AbuseFilterTest.php', 'Flow\\Tests\\SpamFilter\\ConfirmEditTest' => __DIR__ . '/tests/phpunit/SpamFilter/ConfirmEditTest.php', 'Flow\\Tests\\SpamFilter\\ContentLengthFilterTest' => __DIR__ . '/tests/phpunit/SpamFilter/ContentLengthFilterTest.php', 'Flow\\Tests\\SpamFilter\\SpamBlacklistTest' => __DIR__ . '/tests/phpunit/SpamFilter/SpamBlacklistTest.php', 'Flow\\Tests\\SpamFilter\\SpamRegexTest' => __DIR__ . '/tests/phpunit/SpamFilter/SpamRegexTest.php', 'Flow\\Tests\\TemplateHelperTest' => __DIR__ . '/tests/phpunit/TemplateHelperTest.php', 'Flow\\Tests\\TemplatingTest' => __DIR__ . '/tests/phpunit/TemplatingTest.php', 'Flow\\Tests\\UrlGeneratorTest' => __DIR__ . '/tests/phpunit/UrlGeneratorTest.php', 'Flow\\Tests\\WatchedTopicItemTest' => __DIR__ . '/tests/phpunit/WatchedTopicItemsTest.php', 'Flow\\UrlGenerator' => __DIR__ . '/includes/UrlGenerator.php', 'Flow\\Utils\\NamespaceIterator' => __DIR__ . '/includes/Utils/NamespaceIterator.php', 'Flow\\Utils\\PagesWithPropertyIterator' => __DIR__ . '/includes/Utils/PagesWithPropertyIterator.php', 'Flow\\View' => __DIR__ . '/includes/View.php', 'Flow\\WatchedTopicItems' => __DIR__ . '/includes/WatchedTopicItems.php', 'Flow\\WorkflowLoader' => __DIR__ . '/includes/WorkflowLoader.php', 'Flow\\WorkflowLoaderFactory' => __DIR__ . '/includes/WorkflowLoaderFactory.php', - 'LCRun3' => __DIR__ . '/vendor/lightncandy.php', - 'LightnCandy' => __DIR__ . '/vendor/lightncandy.php', 'MaintenanceDebugLogger' => __DIR__ . '/maintenance/MaintenanceDebugLogger.php', 'Pimple' => __DIR__ . '/vendor/Pimple.php', ); diff --git a/vendor/lightncandy.php b/vendor/lightncandy.php deleted file mode 100644 index d7a62e578c..0000000000 --- a/vendor/lightncandy.php +++ /dev/null @@ -1,2560 +0,0 @@ - - */ - -/** - * LightnCandy static core class. - */ -class LightnCandy { - // Compile time error handling flags - const FLAG_ERROR_LOG = 1; - const FLAG_ERROR_EXCEPTION = 2; - const FLAG_ERROR_SKIPPARTIAL = 4194304; - - // Compile the template as standalone PHP code which can execute without including LightnCandy - const FLAG_STANDALONE = 4; - const FLAG_NOESCAPE = 67108864; - - // JavaScript compatibility - const FLAG_JSTRUE = 8; - const FLAG_JSOBJECT = 16; - - // Handlebars.js compatibility - const FLAG_THIS = 32; - const FLAG_WITH = 64; - const FLAG_PARENT = 128; - const FLAG_JSQUOTE = 256; - const FLAG_ADVARNAME = 512; - const FLAG_SPACECTL = 1024; - const FLAG_NAMEDARG = 2048; - const FLAG_SPVARS = 4096; - const FLAG_SLASH = 8388608; - const FLAG_ELSE = 16777216; - - // PHP behavior flags - const FLAG_EXTHELPER = 8192; - const FLAG_ECHO = 16384; - const FLAG_PROPERTY = 32768; - const FLAG_METHOD = 65536; - const FLAG_RUNTIMEPARTIAL = 1048576; - - // Mustache compatibility - const FLAG_MUSTACHESP = 131072; - const FLAG_MUSTACHELOOKUP = 262144; - const FLAG_MUSTACHEPAIN = 2097152; - const FLAG_MUSTACHESEC = 33554432; - - // Template rendering time debug flags - const FLAG_RENDER_DEBUG = 524288; - - // alias flags - const FLAG_BESTPERFORMANCE = 16384; // FLAG_ECHO - const FLAG_JS = 24; // FLAG_JSTRUE + FLAG_JSOBJECT - const FLAG_MUSTACHE = 40239104; // FLAG_ERROR_SKIPPARTIAL + FLAG_MUSTACHESP + FLAG_MUSTACHELOOKUP + FLAG_MUSTACHEPAIN + FLAG_MUSTACHESEC - const FLAG_HANDLEBARS = 27402208; // FLAG_THIS + FLAG_WITH + FLAG_PARENT + FLAG_JSQUOTE + FLAG_ADVARNAME + FLAG_SPACECTL + FLAG_NAMEDARG + FLAG_SPVARS + FLAG_SLASH + FLAG_ELSE + FLAG_MUSTACHESP + FLAG_MUSTACHEPAIN - const FLAG_HANDLEBARSJS = 27402232; // FLAG_JS + FLAG_HANDLEBARS - const FLAG_INSTANCE = 98304; // FLAG_PROPERTY + FLAG_METHOD - - // RegExps - const VARNAME_SEARCH = '/(\\[[^\\]]+\\]|[^\\[\\]\\.]+)/'; - const EXTENDED_COMMENT_SEARCH = '/{{!--.*?--}}/s'; - - // Positions of matched token - const POS_LOTHER = 1; - const POS_LSPACE = 2; - const POS_BEGINTAG = 3; - const POS_LSPACECTL = 4; - const POS_OP = 5; - const POS_INNERTAG = 6; - const POS_RSPACECTL = 7; - const POS_ENDTAG = 8; - const POS_RSPACE = 9; - const POS_ROTHER = 10; - - protected static $lastContext; - - /** - * Compile handlebars template into PHP code. - * - * @param string $template handlebars template string - * @param array $options LightnCandy compile time and run time options, default is array('flags' => LightnCandy::FLAG_BESTPERFORMANCE) - * - * @return string|false Compiled PHP code when successed. If error happened and compile failed, return false. - */ - public static function compile($template, $options = array('flags' => self::FLAG_BESTPERFORMANCE)) { - $context = static::buildContext($options); - - if (static::handleError($context)) { - return false; - } - - // Strip extended comments - $template = preg_replace(static::EXTENDED_COMMENT_SEARCH, '{{!*}}', $template); - - // Do first time scan to find out used feature, detect template error. - static::setupToken($context); - static::verifyTemplate($context, $template); - - if (static::handleError($context)) { - return false; - } - - // Do PHP code generation. - static::setupToken($context); - $code = static::compileTemplate($context, static::escapeTemplate($template)); - - // return false when fatal error - if (static::handleError($context)) { - return false; - } - - // Or, return full PHP render codes as string - return static::composePHPRender($context, $code); - } - - /* - * Escape template - * - * @param string $template handlebars template string - * - * @return string Escaped template - * - * @expect 'abc' when input 'abc' - * @expect 'a\\bc' when input 'a\bc' - * @expect 'a\\\'bc' when input 'a\'bc' - */ - protected static function escapeTemplate($template) { - return addcslashes(addcslashes($template, '\\'), "'"); - } - - /** - * Setup token delimiter by default or provided string - * - * @param array $context Current context - * @param string $left left string of a token - * @param string $right right string of a token - */ - protected static function setupToken(&$context, $left = '{{', $right = '}}') { - if (preg_match('/=/', "$left$right")) { - $context['error'][] = "Can not set delimiter contains '=' , you try to set delimiter as '$left' and '$right'."; - return; - } - - $context['tokens']['startchar'] = substr($left, 0, 1); - $context['tokens']['left'] = $left; - $context['tokens']['right'] = $right; - - if (($left === '{{') && ($right === '}}')) { - $left = '\\{{2,3}'; - $right = '\\}{2,3}'; - } else { - $left = preg_quote($left); - $right = preg_quote($right); - } - - $context['tokens']['search'] = "/^(.*?)(\\s*)($left)(~?)([\\^#\\/!&>]?)(.*?)(~?)($right)(\\s*)(.*)\$/s"; - } - - /** - * Verify template and scan for used features - * - * @param array $context Current context - * @param string $template handlebars template - */ - protected static function verifyTemplate(&$context, $template) { - while (preg_match($context['tokens']['search'], $template, $matches)) { - $context['tokens']['count']++; - static::scanFeatures($matches, $context); - $template = $matches[self::POS_ROTHER]; - } - } - - /** - * Compile template into PHP code (internal method) - * - * @param array $context Current context - * @param string $template handlebars template - * @param string $partial partial name when $template is come from the template - * - * @return string generated PHP code - */ - protected static function compileTemplate(&$context, $template, $partial = '') { - // Check for recursive partial - if ($partial && !$context['flags']['runpart']) { - $context['partialStack'][] = $partial; - $diff = count($context['partialStack']) - count(array_unique($context['partialStack'])); - if ($diff > 1) { - $context['error'][] = "Skip rendering partial '$partial' again due to recursive detected"; - return ''; - } - if ($diff) { - $context['error'][] = 'I found recursive partial includes as the path: ' . implode(' -> ', $context['partialStack']) . '! You should fix your template or compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag.'; - } - } - - $code = ''; - while (preg_match($context['tokens']['search'], $template, $matches)) { - // Skip a token when it is slash escaped - if ($context['flags']['slash'] && ($matches[self::POS_LSPACE] === '') && preg_match('/^(.*?)(\\\\+)$/s', $matches[self::POS_LOTHER], $escmatch)) { - if (strlen($escmatch[2]) % 4) { - $code .= substr($matches[self::POS_LOTHER], 0, -2) . $context['tokens']['startchar']; - $matches[self::POS_BEGINTAG] = substr($matches[self::POS_BEGINTAG], 1); - $template = implode('', array_slice($matches, self::POS_BEGINTAG)); - continue; - } else { - $matches[self::POS_LOTHER] = $escmatch[1] . str_repeat('\\', strlen($escmatch[2]) / 2); - } - } - - $context['tokens']['current']++; - $tmpl = static::compileToken($matches, $context); - if ($tmpl == $context['ops']['seperator']) { - $tmpl = ''; - } else { - $tmpl = "'$tmpl'"; - } - $code .= "{$matches[self::POS_LOTHER]}{$matches[self::POS_LSPACE]}$tmpl"; - $template = "{$matches[self::POS_RSPACE]}{$matches[self::POS_ROTHER]}"; - } - - if ($partial && !$context['flags']['runpart']) { - array_pop($context['partialStack']); - } - - return "$code$template"; - } - - /** - * Compose LightnCandy render codes for include() - * - * @param array $context Current context - * @param string $code generated PHP code - * - * @return string Composed PHP code - */ - protected static function composePHPRender($context, $code) { - $flagJStrue = static::getBoolStr($context['flags']['jstrue']); - $flagJSObj = static::getBoolStr($context['flags']['jsobj']); - $flagSPVar = static::getBoolStr($context['flags']['spvar']); - $flagProp = static::getBoolStr($context['flags']['prop']); - $flagMethod = static::getBoolStr($context['flags']['method']); - $flagMustlok = static::getBoolStr($context['flags']['mustlok']); - $flagMustsec = static::getBoolStr($context['flags']['mustsec']); - $flagEcho = static::getBoolStr($context['flags']['echo']); - - $libstr = static::exportLCRun($context); - $constants = static::exportLCRunConstant($context); - $helpers = static::exportHelper($context); - $bhelpers = static::exportHelper($context, 'blockhelpers'); - $hbhelpers = static::exportHelper($context, 'hbhelpers'); - $debug = LCRun3::DEBUG_ERROR_LOG; - - // Return generated PHP code string. - return " array( - 'jstrue' => $flagJStrue, - 'jsobj' => $flagJSObj, - 'spvar' => $flagSPVar, - 'prop' => $flagProp, - 'method' => $flagMethod, - 'mustlok' => $flagMustlok, - 'mustsec' => $flagMustsec, - 'echo' => $flagEcho, - 'debug' => \$debugopt, - ), - 'constants' => $constants, - 'helpers' => $helpers, - 'blockhelpers' => $bhelpers, - 'hbhelpers' => $hbhelpers, - 'partials' => array({$context['partialCode']}), - 'scopes' => array(\$in), - 'sp_vars' => array('root' => \$in), -$libstr - ); - {$context['renderex']} - {$context['ops']['op_start']}'$code'{$context['ops']['op_end']} -} -?>"; - } - - /** - * Build context from options - * - * @param array $options input options - * - * @return array Context from options - */ - protected static function buildContext($options) { - if (!is_array($options)) { - $options = array(); - } - - $flags = isset($options['flags']) ? $options['flags'] : self::FLAG_BESTPERFORMANCE; - - $context = array( - 'flags' => array( - 'errorlog' => $flags & self::FLAG_ERROR_LOG, - 'exception' => $flags & self::FLAG_ERROR_EXCEPTION, - 'skippartial' => $flags & self::FLAG_ERROR_SKIPPARTIAL, - 'standalone' => $flags & self::FLAG_STANDALONE, - 'noesc' => $flags & self::FLAG_NOESCAPE, - 'jstrue' => $flags & self::FLAG_JSTRUE, - 'jsobj' => $flags & self::FLAG_JSOBJECT, - 'jsquote' => $flags & self::FLAG_JSQUOTE, - 'this' => $flags & self::FLAG_THIS, - 'with' => $flags & self::FLAG_WITH, - 'parent' => $flags & self::FLAG_PARENT, - 'echo' => $flags & self::FLAG_ECHO, - 'advar' => $flags & self::FLAG_ADVARNAME, - 'namev' => $flags & self::FLAG_NAMEDARG, - 'spvar' => $flags & self::FLAG_SPVARS, - 'slash' => $flags & self::FLAG_SLASH, - 'else' => $flags & self::FLAG_ELSE, - 'exhlp' => $flags & self::FLAG_EXTHELPER, - 'mustsp' => $flags & self::FLAG_MUSTACHESP, - 'mustlok' => $flags & self::FLAG_MUSTACHELOOKUP, - 'mustpi' => $flags & self::FLAG_MUSTACHEPAIN, - 'mustsec' => $flags & self::FLAG_MUSTACHESEC, - 'debug' => $flags & self::FLAG_RENDER_DEBUG, - 'prop' => $flags & self::FLAG_PROPERTY, - 'method' => $flags & self::FLAG_METHOD, - 'runpart' => $flags & self::FLAG_RUNTIMEPARTIAL, - ), - 'level' => 0, - 'stack' => array(), - 'error' => array(), - 'basedir' => static::buildCXBasedir($options), - 'fileext' => static::buildCXFileext($options), - 'tokens' => array( - 'standalone' => true, - 'ahead' => false, - 'current' => 0, - 'count' => 0, - 'partialind' => '', - ), - 'usedPartial' => array(), - 'partialStack' => array(), - 'partialCode' => '', - 'usedFeature' => array( - 'rootthis' => 0, - 'enc' => 0, - 'raw' => 0, - 'sec' => 0, - 'isec' => 0, - 'if' => 0, - 'else' => 0, - 'unless' => 0, - 'each' => 0, - 'this' => 0, - 'parent' => 0, - 'with' => 0, - 'dot' => 0, - 'comment' => 0, - 'partial' => 0, - 'helper' => 0, - 'bhelper' => 0, - 'hbhelper' => 0, - 'delimiter' => 0, - ), - 'usedCount' => array( - 'var' => array(), - 'helpers' => array(), - 'blockhelpers' => array(), - 'hbhelpers' => array(), - 'lcrun' => array(), - ), - 'partials' => (isset($options['partials']) && is_array($options['partials'])) ? $options['partials'] : array(), - 'helpers' => array(), - 'blockhelpers' => array(), - 'hbhelpers' => array(), - 'renderex' => isset($options['renderex']) ? $options['renderex'] : '', - ); - - $context['ops'] = $context['flags']['echo'] ? array( - 'seperator' => ',', - 'f_start' => 'echo ', - 'f_end' => ';', - 'op_start' => 'ob_start();echo ', - 'op_end' => ';return ob_get_clean();', - 'cnd_start' => ';if ', - 'cnd_then' => '{echo ', - 'cnd_else' => ';}else{echo ', - 'cnd_end' => ';}echo ', - ) : array( - 'seperator' => '.', - 'f_start' => 'return ', - 'f_end' => ';', - 'op_start' => 'return ', - 'op_end' => ';', - 'cnd_start' => '.(', - 'cnd_then' => ' ? ', - 'cnd_else' => ' : ', - 'cnd_end' => ').', - ); - - $context['ops']['enc'] = $context['flags']['jsquote'] ? 'encq' : 'enc'; - $context = static::buildHelperTable($context, $options); - $context = static::buildHelperTable($context, $options, 'blockhelpers'); - $context = static::buildHelperTable($context, $options, 'hbhelpers'); - - return $context; - } - - /** - * Build custom helper table - * - * @param array $context prepared context - * @param array $options input options - * @param string $tname helper table name - * - * @return array context with generated helper table - * - * @expect array() when input array(), array() - * @expect array('flags' => array('exhlp' => 1)) when input array('flags' => array('exhlp' => 1)), array('helpers' => array('abc')) - * @expect array('error' => array('Can not find custom helper function defination abc() !'), 'flags' => array('exhlp' => 0)) when input array('error' => array(), 'flags' => array('exhlp' => 0)), array('helpers' => array('abc')) - * @expect array('flags' => array('exhlp' => 1), 'helpers' => array('LCRun3::raw' => 'LCRun3::raw')) when input array('flags' => array('exhlp' => 1), 'helpers' => array()), array('helpers' => array('LCRun3::raw')) - * @expect array('flags' => array('exhlp' => 1), 'helpers' => array('test' => 'LCRun3::raw')) when input array('flags' => array('exhlp' => 1), 'helpers' => array()), array('helpers' => array('test' => 'LCRun3::raw')) - */ - protected static function buildHelperTable($context, $options, $tname = 'helpers') { - if (isset($options[$tname]) && is_array($options[$tname])) { - foreach ($options[$tname] as $name => $func) { - if (is_callable($func)) { - $context[$tname][is_int($name) ? $func : $name] = $func; - } else { - if (is_array($func)) { - $context['error'][] = "I found an array in $tname with key as $name, please fix it."; - } else { - if (!$context['flags']['exhlp']) { - $context['error'][] = "Can not find custom helper function defination $func() !"; - } - } - } - } - } - return $context; - } - - /** - * Read partial file content as string and store in context - * - * @param string $name partial name - * @param array $context Current context of compiler progress. - */ - protected static function readPartial($name, &$context) { - $context['usedFeature']['partial']++; - - if (isset($context['usedPartial'][$name])) { - return; - } - - $cnt = static::resolvePartial($name, $context); - - if ($cnt !== null) { - return static::compilePartial($name, $context, $cnt); - } - - if (!$context['flags']['skippartial']) { - $context['error'][] = "Can not find partial file for '$name', you should set correct basedir and fileext in options"; - } - } - - /** - * locate partial file, return the file name - * - * @param string $name partial name - * @param array $context Current context of compiler progress. - * - * @return string|null $content partial content - */ - protected static function resolvePartial(&$name, &$context) { - if (isset($context['partials'][$name])) { - return $context['partials'][$name]; - } - - foreach ($context['basedir'] as $dir) { - foreach ($context['fileext'] as $ext) { - $fn = "$dir/$name$ext"; - if (file_exists($fn)) { - return file_get_contents($fn); - } - } - } - return null; - } - - /** - * compile partial file, stored in context - * - * @param string $name partial name - * @param array $context Current context of compiler progress. - * @param string $content partial content - */ - protected static function compilePartial(&$name, &$context, $content) { - $context['usedPartial'][$name] = static::escapeTemplate($content); - - $originalAhead = $context['tokens']['ahead']; - $tmpContext = $context; - $tmpContext['level'] = 0; - static::setupToken($tmpContext); - - static::verifyTemplate($tmpContext, $content); - $originalToken = $context['tokens']; - $context = $tmpContext; - $context['tokens'] = $originalToken; - $context['tokens']['ahead'] = $originalAhead; - - if ($context['flags']['runpart']) { - $code = static::compileTemplate($context, $context['usedPartial'][$name], $name); - if ($context['flags']['mustpi']) { - $sp = ', $sp'; - $code = preg_replace('/^/m', "'{$context['ops']['seperator']}\$sp{$context['ops']['seperator']}'", $code); - } else { - $sp = ''; - } - $context['partialCode'] .= "'$name' => function (\$cx, \$in{$sp}) {{$context['ops']['op_start']}'$code'{$context['ops']['op_end']}},"; - } - } - - /** - * Internal method used by compile(). Check options and handle fileext. - * - * @param array $options current compile option - * - * @return array file extensions - * - * @expect array('.tmpl') when input array() - * @expect array('test') when input array('fileext' => 'test') - * @expect array('test1') when input array('fileext' => array('test1')) - * @expect array('test2', 'test3') when input array('fileext' => array('test2', 'test3')) - */ - protected static function buildCXFileext($options) { - $exts = isset($options['fileext']) ? $options['fileext'] : '.tmpl'; - return is_array($exts) ? $exts : array($exts); - } - - /** - * Internal method used by compile(). Check options and handle basedir. - * - * @param array $options current compile option - * - * @return array base directories - * - * @expect array() when input array() - * @expect array() when input array('basedir' => array()) - * @expect array('src') when input array('basedir' => array('src')) - * @expect array('src') when input array('basedir' => array('src', 'dir_not_found')) - * @expect array('src', 'tests') when input array('basedir' => array('src', 'tests')) - */ - protected static function buildCXBasedir($options) { - $dirs = isset($options['basedir']) ? $options['basedir'] : 0; - $dirs = is_array($dirs) ? $dirs : array($dirs); - $ret = array(); - - foreach ($dirs as $dir) { - if (is_string($dir) && is_dir($dir)) { - $ret[] = $dir; - } - } - - return $ret; - } - - /** - * Internal method used by compile(). Get PHP code from a closure of function as string. - * - * @param object $closure Closure object - * - * @return string - * - * @expect 'function($a) {return;}' when input function ($a) {return;} - * @expect 'function($a) {return;}' when input function ($a) {return;} - */ - protected static function getPHPCode($closure) { - if (is_string($closure) && preg_match('/(.+)::(.+)/', $closure, $matched)) { - $ref = new ReflectionMethod($matched[1], $matched[2]); - } else { - $ref = new ReflectionFunction($closure); - } - $fname = $ref->getFileName(); - - $lines = file_get_contents($fname); - $file = new SplFileObject($fname); - $file->seek($ref->getStartLine() - 2); - $spos = $file->ftell(); - $file->seek($ref->getEndLine() - 1); - $epos = $file->ftell(); - - return preg_replace('/^.*?function(\s+[^\s\\(]+?)?\s*?\\((.+?)\\}[,\\s]*;?$/s', 'function($2}', substr($lines, $spos, $epos - $spos)); - } - - /** - * Internal method used by compile(). Export required custom helper functions. - * - * @param string $tname helper table name - * @param array $context current compile context - * - * @return string - */ - protected static function exportHelper($context, $tname = 'helpers') { - $ret = ''; - foreach ($context[$tname] as $name => $func) { - if (!isset($context['usedCount'][$tname][$name])) { - continue; - } - if ((is_object($func) && ($func instanceof Closure)) || ($context['flags']['exhlp'] == 0)) { - $ret .= (" '$name' => " . static::getPHPCode($func) . ",\n"); - continue; - } - $ret .= " '$name' => '$func',\n"; - } - - return "array($ret)"; - } - - /** - * Internal method used by compile(). Export required standalone functions. - * - * @param array $context current compile context - * - * @return string - */ - protected static function exportLCRun($context) { - if ($context['flags']['standalone'] == 0) { - return ''; - } - - $class = new ReflectionClass('LCRun3'); - $fname = $class->getFileName(); - $lines = file_get_contents($fname); - $file = new SplFileObject($fname); - $methods = array(); - $ret = "'funcs' => array(\n"; - - foreach ($class->getMethods() as $method) { - $name = $method->getName(); - $file->seek($method->getStartLine() - 2); - $spos = $file->ftell(); - $file->seek($method->getEndLine() - 2); - $epos = $file->ftell(); - $methods[$name] = static::scanLCRunDependency($context, preg_replace('/public static function (.+)\\(/', '\'$1\' => function (', substr($lines, $spos, $epos - $spos))); - } - unset($file); - - $exports = array_keys($context['usedCount']['lcrun']); - - while (true) { - if (array_sum(array_map(function ($name) use (&$exports, $methods) { - $n = 0; - foreach ($methods[$name][1] as $child => $count) { - if (!in_array($child, $exports)) { - $exports[] = $child; - $n++; - } - } - return $n; - }, $exports)) == 0) { - break; - } - } - - foreach ($exports as $export) { - $ret .= ($methods[$export][0] . " },\n"); - } - - return "$ret)\n"; - } - - /** - * Internal method used by compile(). Export standalone constants. - * - * @param array $context current compile context - * - * @return string - */ - protected static function exportLCRunConstant($context) { - if ($context['flags']['standalone'] == 0) { - return 'array()'; - } - - $class = new ReflectionClass('LCRun3'); - $constants = $class->getConstants(); - $ret = " array(\n"; - foreach($constants as $name => $value) { - $ret .= " '$name' => ". (is_string($value) ? "'$value'" : $value ) . ",\n"; - } - $ret .= " )"; - return $ret; - } - - /** - * Internal method used by compile(). Export required standalone functions. - * - * @param array $context current compile context - * @param string $code PHP code string of the method - * - * @return array list of converted code and children array - */ - protected static function scanLCRunDependency($context, $code) { - $child = array(); - - $code = preg_replace_callback('/self::(\w+?)\s*\(/', function ($matches) use ($context, &$child) { - if (!isset($child[$matches[1]])) { - $child[$matches[1]] = 0; - } - $child[$matches[1]]++; - - return "\$cx['funcs']['{$matches[1]}']("; - }, $code); - - // replace the constants - $code = preg_replace('/self::([A-Z0-9_]+)/', "\$cx['constants']['$1']", $code); - return array($code, $child); - } - - /** - * Internal method used by compile(). Handle exists error and return error status. - * - * @param array $context Current context of compiler progress. - * - * @throws Exception - * @return boolean True when error detected - * - * @expect true when input array('level' => 1, 'stack' => array('X'), 'flags' => array('errorlog' => 0, 'exception' => 0), 'error' => array()) - * @expect false when input array('level' => 0, 'error' => array()) - * @expect true when input array('level' => 0, 'error' => array('some error'), 'flags' => array('errorlog' => 0, 'exception' => 0)) - */ - protected static function handleError(&$context) { - if ($context['level'] > 0) { - $token = array_pop($context['stack']); - $context['error'][] = "Unclosed token {{{#$token}}} !!"; - } - - static::$lastContext = $context; - - if (count($context['error'])) { - if ($context['flags']['errorlog']) { - error_log(implode("\n", $context['error'])); - } - if ($context['flags']['exception']) { - throw new Exception(implode("\n", $context['error'])); - } - return true; - } - return false; - } - - /** - * Internal method used by compile(). Return 'true' or 'false' string. - * - * @param integer $v value - * - * @return string 'true' when the value larger then 0 - * - * @expect 'true' when input 1 - * @expect 'true' when input 999 - * @expect 'false' when input 0 - * @expect 'false' when input -1 - */ - protected static function getBoolStr($v) { - return ($v > 0) ? 'true' : 'false'; - } - - /** - * Get last compiler context. - * - * @return array Context data - */ - public static function getContext() { - return static::$lastContext; - } - - /** - * Get a working render function by a string of PHP code. This method may requires php setting allow_url_include=1 and allow_url_fopen=1 , or access right to tmp file system. - * - * @param string $php PHP code - * @param string|null $tmpDir Optional, change temp directory for php include file saved by prepare() when cannot include PHP code with data:// format. - * - * @return Closure|false result of include() - * - * @deprecated - */ - public static function prepare($php, $tmpDir = null) { - if (!ini_get('allow_url_include') || !ini_get('allow_url_fopen')) { - if (!$tmpDir || !is_dir($tmpDir)) { - $tmpDir = sys_get_temp_dir(); - } - } - - if ($tmpDir) { - $fn = tempnam($tmpDir, 'lci_'); - if (!$fn) { - error_log("Can not generate tmp file under $tmpDir!!\n"); - return false; - } - if (!file_put_contents($fn, $php)) { - error_log("Can not include saved temp php code from $fn, you should add $tmpDir into open_basedir!!\n"); - return false; - } - return include($fn); - } - - return include('data://text/plain,' . urlencode($php)); - } - - /** - * Internal method used by compile(). Get function name for standalone or none standalone template. - * - * @param array $context Current context of compiler progress. - * @param string $name base function name - * @param string $tag original handlabars tag for debug - * - * @return string compiled Function name - * - * @expect 'LCRun3::test(' when input array('flags' => array('standalone' => 0, 'debug' => 0)), 'test', '' - * @expect 'LCRun3::test2(' when input array('flags' => array('standalone' => 0, 'debug' => 0)), 'test2', '' - * @expect "\$cx['funcs']['test3'](" when input array('flags' => array('standalone' => 1, 'debug' => 0)), 'test3', '' - * @expect 'LCRun3::debug(\'abc\', \'test\', ' when input array('flags' => array('standalone' => 0, 'debug' => 1)), 'test', 'abc' - */ - protected static function getFuncName(&$context, $name, $tag) { - static::addUsageCount($context, 'lcrun', $name); - - if ($context['flags']['debug'] && ($name != 'miss')) { - $dbg = "'$tag', '$name', "; - $name = 'debug'; - static::addUsageCount($context, 'lcrun', 'debug'); - } else { - $dbg = ''; - } - - return $context['flags']['standalone'] ? "\$cx['funcs']['$name']($dbg" : "LCRun3::$name($dbg"; - } - - /** - * Internal method used by getArrayCode(). Get variable names translated string. - * - * @param array $scopes an array of variable names with single quote - * - * @return string PHP array names string - * - * @expect '' when input array() - * @expect '[a]' when input array('a') - * @expect '[a][b][c]' when input array('a', 'b', 'c') - */ - protected static function getArrayStr($scopes) { - return count($scopes) ? '[' . implode('][', $scopes) . ']' : ''; - } - - /** - * Internal method used by getVariableName(). Get variable names translated string. - * - * @param array $list an array of variable names. - * - * @return string PHP array names string - * - * @expect '' when input array() - * @expect "['a']" when input array('a') - * @expect "['a']['b']['c']" when input array('a', 'b', 'c') - */ - protected static function getArrayCode($list) { - return static::getArrayStr(array_map(function ($v) { - return "'$v'"; - }, $list)); - } - - /** - * Internal method used by compile(). - * - * @param array $vn variable name array. - * @param array $context current compile context - * @param boolean $ishelper true when compile for helper - * - * @return array variable names - * - * @expect array('array(array($in),array())', array('this')) when input array(null), array('flags'=>array('spvar'=>true)) - * @expect array('array(array($in,$in),array())', array('this', 'this')) when input array(null, null), array('flags'=>array('spvar'=>true)) - * @expect array('array(array(),array(\'a\'=>$in))', array('this')) when input array('a' => null), array('flags'=>array('spvar'=>true)) - */ - protected static function getVariableNames($vn, &$context, $ishelper = false) { - $vars = array(array(), array()); - $exps = array(); - foreach ($vn as $i => $v) { - if (isset($v[0]) && preg_match('/^\(.+\)$/', $v[0])) { - $V = static::compileSubExpression($v[0], $context); - } else { - $V = static::getVariableName($v, $context, $ishelper); - } - if (is_string($i)) { - $vars[1][] = "'$i'=>{$V[0]}"; - } else { - $vars[0][] = $V[0]; - } - $exps[] = $V[1]; - } - return array('array(array(' . implode(',', $vars[0]) . '),array(' . implode(',', $vars[1]) . '))', $exps); - } - - /** - * Internal method used by compile(). - * - * @param string $subExpression subExpression to compile - * @param array $context current compile context - * - * @return array code representing passed expression - */ - protected static function compileSubExpression($subExpression, &$context) { - // mock up a token for this expression - $token = array_fill(self::POS_LOTHER, self::POS_ROTHER, ''); - - // strip outer ( ) from subexpression - $token[self::POS_INNERTAG] = substr($subExpression, 1, -1); - - list(, $vars) = static::parseTokenArgs($token, $context); - - // no separator is needed, this code will be used as a function argument - $origSeperator = $context['ops']['seperator']; - $context['ops']['seperator'] = ''; - // override $raw, subexpressions are never escaped - $ret = static::compileCustomHelper($context, $vars, true, true); - $context['ops']['seperator'] = $origSeperator; - - return array($ret ? $ret : '', $subExpression); - } - - /** - * Internal method used by compile(). - * - * @param array $var variable parsed path - * @param array $context current compile context - * @param boolean $ishelper true when compile for helper$ - * - * @return array variable names - * - * @expect array('$in', 'this') when input array(null), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array('true', 'true') when input array('true'), array('flags'=>array('spvar'=>true,'debug'=>0)), true - * @expect array('false', 'false') when input array('false'), array('flags'=>array('spvar'=>true,'debug'=>0)), true - * @expect array(2, '2') when input array('2'), array('flags'=>array('spvar'=>true,'debug'=>0)), true - * @expect array('((isset($in[\'@index\']) && is_array($in)) ? $in[\'@index\'] : null)', '[@index]') when input array('@index'), array('flags'=>array('spvar'=>false,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0)) - * @expect array("((isset(\$cx['sp_vars']['index']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['index'] : null)", '@[index]') when input array('@index'), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array("((isset(\$cx['sp_vars']['key']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['key'] : null)", '@[key]') when input array('@key'), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array("((isset(\$cx['sp_vars']['first']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['first'] : null)", '@[first]') when input array('@first'), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array("((isset(\$cx['sp_vars']['last']) && is_array(\$cx['sp_vars'])) ? \$cx['sp_vars']['last'] : null)", '@[last]') when input array('@last'), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array('\'a\'', '"a"') when input array('"a"'), array('flags'=>array('spvar'=>true,'debug'=>0)) - * @expect array('((isset($in[\'a\']) && is_array($in)) ? $in[\'a\'] : null)', '[a]') when input array('a'), array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0)) - * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-1])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-1][\'a\'] : null)', '../[a]') when input array(1,'a'), array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0)) - * @expect array('((isset($cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\']) && is_array($cx[\'scopes\'][count($cx[\'scopes\'])-3])) ? $cx[\'scopes\'][count($cx[\'scopes\'])-3][\'a\'] : null)', '../../../[a]') when input array(3,'a'), array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0)) - * @expect array('((isset($in[\'id\']) && is_array($in)) ? $in[\'id\'] : null)', 'this.[id]') when input array(null, 'id'), array('flags'=>array('spvar'=>true,'debug'=>0,'prop'=>0,'method'=>0,'mustlok'=>0)) - * @expect array('LCRun3::v($cx, $in, array(\'id\'))', 'this.[id]') when input array(null, 'id'), array('flags'=>array('prop'=>true,'spvar'=>true,'debug'=>0,'method'=>0,'mustlok'=>0,'standalone'=>0)) - */ - protected static function getVariableName($var, &$context, $ishelper = false) { - if (isset($var[0])) { - // Handle language constants or number , only for helpers - if ($ishelper) { - if ((count($var) == 1) && is_numeric($var[0])) { - // convert 0x00 or 0b00 numbers to decimal - return array((string) 1 * $var[0], $var[0]); - } - switch ($var[0]) { - case 'true': - return array('true', 'true'); - case 'false': - return array('false', 'false'); - } - } - - // Handle double quoted string - if (preg_match('/^"(.*)"$/', $var[0], $matched)) { - $t = addcslashes(stripslashes(preg_replace('/\\\\\\\\/', '\\', $matched[1])), "'"); - return array("'{$t}'", "\"{$t}\""); - } - } - - $levels = 0; - $base = '$in'; - $spvar = false; - - if (isset($var[0])) { - // trace to parent - if (!is_string($var[0]) && is_int($var[0])) { - $levels = array_shift($var); - } - - // handle @root, @index, @key, @last, etc - if ($context['flags']['spvar']) { - if (substr($var[0], 0, 1) === '@') { - $spvar = true; - $base = "\$cx['sp_vars']"; - $var[0] = substr($var[0], 1); - } - } - - // change base when trace to parent - if ($levels > 0) { - if ($spvar) { - $base .= str_repeat("['_parent']", $levels); - } else { - $base = "\$cx['scopes'][count(\$cx['scopes'])-$levels]"; - } - } - } - - // Generate normalized expression for debug - $exp = static::getExpression($levels, $spvar, $var); - - if ((count($var) == 0) || (is_null($var[0]) && (count($var) == 1))) { - return array($base, $exp); - } - - if (is_null($var[0])) { - array_shift($var); - } - - // 1. To support recursive context lookup... - // 2. To support instance properties or methods... - // the only way is using slower rendering time variable resolver. - if ($context['flags']['prop'] || $context['flags']['method'] || $context['flags']['mustlok']) { - return array(static::getFuncName($context, 'v', $exp) . "\$cx, $base, array(" . implode(',', array_map(function ($V) { - return "'$V'"; - }, $var)) . '))', $exp); - } - - $n = static::getArrayCode($var); - array_pop($var); - $p = count($var) ? static::getArrayCode($var) : ''; - - return array("((isset($base$n) && is_array($base$p)) ? $base$n : " . ($context['flags']['debug'] ? (static::getFuncName($context, 'miss', '') . "\$cx, '$exp')") : 'null' ) . ')', $exp); - } - - /** - * Internal method used by compile(). - * - * @param integer $levels trace N levels top parent scope - * @param boolean $spvar is the path start with @ or not - * @param array $var variable parsed path - * - * @return string normalized expression for debug display - * - * @expect '[a].[b]' when input 0, false, array('a', 'b') - * @expect '@[root]' when input 0, true, array('root') - * @expect 'this' when input 0, false, null - * @expect 'this.[id]' when input 0, false, array(null, 'id') - * @expect '@[root].[a].[b]' when input 0, true, array('root', 'a', 'b') - * @expect '../../[a].[b]' when input 2, false, array('a', 'b') - * @expect '../[a\'b]' when input 1, false, array('a\'b') - */ - protected static function getExpression($levels, $spvar, $var) { - return ($spvar ? '@' : '') . str_repeat('../', $levels) . ((is_array($var) && count($var)) ? implode('.', array_map(function($v) { - return is_null($v) ? 'this' : "[$v]"; - }, $var)) : 'this'); - } - - /** - * Internal method used by compile(). Return array presentation for a variable name - * - * @param string $v variable name to be fixed. - * @param array $context Current compile content. - * - * @return array Return variable name array - * - * @expect array('this') when input 'this', array('flags' => array('advar' => 0, 'this' => 0)) - * @expect array(null) when input 'this', array('flags' => array('advar' => 0, 'this' => 1)) - * @expect array(1, null) when input '../', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(1, null) when input '../.', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(1, null) when input '../this', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(1, 'a') when input '../a', array('flags' => array('advar' => 0, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(2, 'a', 'b') when input '../../a.b', array('flags' => array('advar' => 0, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(2, '[a]', 'b') when input '../../[a].b', array('flags' => array('advar' => 0, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(2, 'a', 'b') when input '../../[a].b', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array('"a.b"') when input '"a.b"', array('flags' => array('advar' => 1, 'this' => 0, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - * @expect array(null, 'id') when input 'this.id', array('flags' => array('advar' => 1, 'this' => 1, 'parent' => 1), 'usedFeature' => array('parent' => 0)) - */ - protected static function fixVariable($v, &$context) { - $ret = array(); - $levels = 0; - - // handle double quoted string - if (preg_match('/^"(.*)"$/', $v, $matched)) { - return array($v); - } - - // handle single quoted string - if (preg_match('/^\\\\\'(.*)\\\\\'$/', $v, $matched)) { - return array("\"{$matched[1]}\""); - } - - // handle .. - if ($v === '..') { - $v = '../'; - } - - // Trace to parent for ../ N times - $v = preg_replace_callback('/\\.\\.\\//', function() use (&$levels) { - $levels++; - return ''; - }, trim($v)); - - if ($levels) { - $ret[] = $levels; - if (!$context['flags']['parent']) { - $context['error'][] = 'Do not support {{../var}}, you should do compile with LightnCandy::FLAG_PARENT flag'; - } - $context['usedFeature']['parent']++; - } - - if ($context['flags']['advar'] && preg_match('/\\]/', $v)) { - preg_match_all(self::VARNAME_SEARCH, $v, $matchedall); - } else { - preg_match_all('/([^\\.\\/]+)/', $v, $matchedall); - } - - if (($v === '.') || ($v === '')) { - $matchedall = array(array('.'), array('.')); - } - - foreach ($matchedall[1] as $m) { - if ($context['flags']['advar'] && substr($m, 0, 1) === '[') { - $ret[] = substr($m, 1, -1); - } else { - $ret[] = (($context['flags']['this'] && ($m === 'this')) || ($m === '.')) ? null : $m; - } - } - - return $ret; - } - - /** - * Internal method used by scanFeatures() and compile(). Parse the token and return parsed result. - * - * @param array $token preg_match results - * @param array $context current compile context - * - * @return array Return parsed result - * - * @expect array(false, array(array(null))) when input array(0,0,0,0,0,0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(true, array(array(null))) when input array(0,0,0,'{{{',0,0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(true, array(array(null))) when input array(0,0,0,0,0,0,''), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 1)) - * @expect array(false, array(array('a'))) when input array(0,0,0,0,0,0,'a'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('b'))) when input array(0,0,0,0,0,0,'a b'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('"b'), array('c"'))) when input array(0,0,0,0,0,0,'a "b c"'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('"b c"'))) when input array(0,0,0,0,0,0,'a "b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('[b'), array('c]'))) when input array(0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('[b'), array('c]'))) when input array(0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('b c'))) when input array(0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 0, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('b c'))) when input array(0,0,0,0,0,0,'a [b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), 'q' => array('b c'))) when input array(0,0,0,0,0,0,'a q=[b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), array('q=[b c'))) when input array(0,0,0,0,0,0,'a [q=[b c]'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), 'q' => array('[b'), array('c]'))) when input array(0,0,0,0,0,0,'a q=[b c]'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), 'q' => array('b'), array('c'))) when input array(0,0,0,0,0,0,'a [q]=b c'), array('flags' => array('advar' => 0, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('a'), 'q' => array('"b c"'))) when input array(0,0,0,0,0,0,'a q="b c"'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('(foo bar)'))) when input array(0,0,0,0,0,0,'(foo bar)'), array('flags' => array('advar' => 1, 'this' => 1, 'namev' => 1, 'noesc' => 0)) - * @expect array(false, array(array('foo'), array("'=='"), array('bar'))) when input array(0,0,0,0,0,0,"foo '==' bar"), array('flags' => array('advar' => 1, 'namev' => 1)) - */ - protected static function parseTokenArgs(&$token, &$context) { - trim($token[self::POS_INNERTAG]); - - // Handle delimiter change - if (preg_match('/^=\s*([^ ]+)\s+([^ ]+)\s*=$/', $token[self::POS_INNERTAG], $matched)) { - static::setupToken($context, $matched[1], $matched[2]); - $token[self::POS_OP] = ' '; - return array(false, array()); - } - - $vars = array(); - $count = preg_match_all('/(\s*)([^\s]+)/', $token[self::POS_INNERTAG], $matchedall); - - // Parse arguments and deal with "..." or [...] or (...) - if (($count > 0) && $context['flags']['advar']) { - $prev = ''; - $expect = 0; - foreach ($matchedall[2] as $index => $t) { - // continue from previous match when expect something - if ($expect) { - $prev .= "{$matchedall[1][$index]}$t"; - // end an argument when end with expected charactor - if (substr($t, -1, 1) === $expect) { - $vars[] = $prev; - $prev = ''; - $expect = 0; - } - continue; - } - - // continue to next match when begin with '(' without ending ')' - if (preg_match('/^\([^\)]+$/', $t)) { - $prev = $t; - $expect = ')'; - continue; - } - - // continue to next match when begin with '"' without ending '"' - if (preg_match('/^"[^"]+$/', $t)) { - $prev = $t; - $expect = '"'; - continue; - } - - // continue to next match when '="' exists without ending '"' - if (preg_match('/="[^"]+$/', $t)) { - $prev = $t; - $expect = '"'; - continue; - } - - // continue to next match when begin with \' without ending ' - if (preg_match('/^\\\\\'[^\']+$/', $t)) { - $prev = $t; - $expect = '\''; - continue; - } - - // continue to next match when =\' exists without ending ' - if (preg_match('/=\\\\\'[^\']+$/', $t)) { - $prev = $t; - $expect = '\''; - continue; - } - - // continue to next match when '[' exists without ending ']' - if (preg_match('/\\[[^\\]]+$/', $t)) { - $prev = $t; - $expect = ']'; - continue; - } - $vars[] = $t; - } - } else { - $vars = ($count > 0) ? $matchedall[2] : explode(' ', $token[self::POS_INNERTAG]); - } - - // Check for advanced variable. - $ret = array(); - $i = 0; - foreach ($vars as $idx => $var) { - // Skip advanced processing for subexpressions - if (preg_match('/^\(.+\)$/', $var)) { - $ret[$i] = array($var); - $i++; - continue; - } - - if ($context['flags']['namev']) { - if (preg_match('/^((\\[([^\\]]+)\\])|([^=^["\']+))=(.+)$/', $var, $m)) { - if (!$context['flags']['advar'] && $m[3]) { - $context['error'][] = "Wrong argument name as '[$m[3]]' in " . static::tokenString($token) . ' ! You should fix your template or compile with LightnCandy::FLAG_ADVARNAME flag.'; - } - $idx = $m[3] ? $m[3] : $m[4]; - $var = $m[5]; - } - } - if ($context['flags']['advar']) { - // foo] Rule 1: no starting [ or [ not start from head - if (preg_match('/^[^\\[\\.]+[\\]\\[]/', $var) - // [bar Rule 2: no ending ] or ] not in the end - || preg_match('/[\\[\\]][^\\]\\.]+$/', $var) - // ]bar. Rule 3: middle ] not before . - || preg_match('/\\][^\\]\\[\\.]+\\./', $var) - // .foo[ Rule 4: middle [ not after . - || preg_match('/\\.[^\\]\\[\\.]+\\[/', preg_replace('/^(..\\/)+/', '', preg_replace('/\\[[^\\]]+\\]/', '[XXX]', $var))) - ) { - $context['error'][] = "Wrong variable naming as '$var' in " . static::tokenString($token) . ' !'; - } - } - - if (($idx === 0) && ($token[self::POS_OP] === '>')) { - $var = array(preg_replace('/^("(.+)")|(\\[(.+)\\])$/', '$2$4', $var)); - } else if (is_numeric($var)) { - $var = array('"' . $var . '"'); - } else { - $var = static::fixVariable($var, $context); - } - - if (is_string($idx)) { - $ret[$idx] = $var; - } else { - $ret[$i] = $var; - $i++; - } - } - - return array(($token[self::POS_BEGINTAG] === '{{{') || ($token[self::POS_OP] === '&') || $context['flags']['noesc'], $ret); - } - - /** - * Internal method used by scanFeatures(). return token string - * - * @param string[] $token detected handlebars {{ }} token - * @param integer $remove remove how many heading and ending token - * - * @return string Return whole token - * - * @expect 'b' when input array(0, 'a', 'b', 'c'), 1 - * @expect 'c' when input array(0, 'a', 'b', 'c', 'd', 'e') - */ - protected static function tokenString($token, $remove = 2) { - return implode('', array_slice($token, 1 + $remove, -$remove)); - } - - /** - * Internal method used by scanFeatures(). Validate start and and. - * - * @param string[] $token detected handlebars {{ }} token - * @param array $context current compile context - * - * @return boolean|null Return true when invalid - * - * @expect null when input array_fill(0, 9, ''), array() - * @expect null when input array_fill(0, 9, '}}'), array() - * @expect true when input array_fill(0, 9, '{{{'), array() - */ - protected static function validateStartEnd($token, &$context) { - // {{ }}} or {{{ }} are invalid - if (strlen($token[self::POS_BEGINTAG]) !== strlen($token[self::POS_ENDTAG])) { - $context['error'][] = 'Bad token ' . static::tokenString($token) . ' ! Do you mean {{' . static::tokenString($token, 4) . '}} or {{{' . static::tokenString($token, 4) . '}}}?'; - return true; - } - // {{{# }}} or {{{! }}} or {{{/ }}} or {{{^ }}} are invalid. - if ((strlen($token[self::POS_BEGINTAG]) === 3) && $token[self::POS_OP] && ($token[self::POS_OP] !== '&')) { - $context['error'][] = 'Bad token ' . static::tokenString($token) . ' ! Do you mean {{' . static::tokenString($token, 4) . '}} ?'; - return true; - } - } - - /** - * Internal method used by compile(). Collect handlebars usage information, detect template error. - * - * @param string[] $token detected handlebars {{ }} token - * @param array $context current compile context - * @param array $vars parsed arguments list - * - * @return boolean|integer|null Return true when invalid or detected - * - * @expect null when input array(0, 0, 0, 0, 0, ''), array(), array() - * @expect 2 when input array(0, 0, 0, 0, 0, '^', '...'), array('usedFeature' => array('isec' => 1), 'level' => 0), array(array('foo')) - * @expect 3 when input array(0, 0, 0, 0, 0, '!', '...'), array('usedFeature' => array('comment' => 2)), array() - * @expect true when input array(0, 0, 0, 0, 0, '/'), array('stack' => array(1), 'level' => 1), array() - * @expect 4 when input array(0, 0, 0, 0, 0, '#', '...'), array('usedFeature' => array('sec' => 3), 'level' => 0), array(array('x')) - * @expect 5 when input array(0, 0, 0, 0, 0, '#', '...'), array('usedFeature' => array('if' => 4), 'level' => 0), array(array('if')) - * @expect 6 when input array(0, 0, 0, 0, 0, '#', '...'), array('usedFeature' => array('with' => 5), 'level' => 0, 'flags' => array('with' => 1)), array(array('with')) - * @expect 7 when input array(0, 0, 0, 0, 0, '#', '...'), array('usedFeature' => array('each' => 6), 'level' => 0), array(array('each')) - * @expect 8 when input array(0, 0, 0, 0, 0, '#', '...'), array('usedFeature' => array('unless' => 7), 'level' => 0), array(array('unless')) - * @expect 9 when input array(0, 0, 0, 0, 0, '#', '...'), array('blockhelpers' => array('abc' => ''), 'usedFeature' => array('bhelper' => 8), 'level' => 0), array(array('abc')) - * @expect 10 when input array(0, 0, 0, 0, 0, ' ', '...'), array('usedFeature' => array('delimiter' => 9), 'level' => 0), array() - * @expect 11 when input array(0, 0, 0, 0, 0, '#', '...'), array('hbhelpers' => array('abc' => ''), 'usedFeature' => array('hbhelper' => 10), 'level' => 0), array(array('abc')) - * @expect true when input array(0, 0, 0, 0, 0, '>', '...'), array('basedir' => array('.'), 'fileext' => array('.tmpl'), 'usedFeature' => array('unless' => 7, 'partial' => 7), 'level' => 0, 'flags' => array('skippartial' => 0)), array('test') - */ - protected static function validateOperations($token, &$context, $vars) { - switch ($token[self::POS_OP]) { - case '>': - static::readPartial($vars[0][0], $context); - return true; - - case ' ': - return ++$context['usedFeature']['delimiter']; - - case '^': - if ($vars[0][0]) { - $context['stack'][] = $token[self::POS_INNERTAG]; - $context['level']++; - return ++$context['usedFeature']['isec']; - } - - if (!$context['flags']['else']) { - $context['error'][] = 'Do not support {{^}}, you should do compile with LightnCandy::FLAG_ELSE flag'; - } - return; - - case '/': - array_pop($context['stack']); - $context['level']--; - return true; - - case '!': - return ++$context['usedFeature']['comment']; - - case '#': - $context['stack'][] = $token[self::POS_INNERTAG]; - $context['level']++; - - // detect handlebars custom helpers. - if (isset($context['hbhelpers'][$vars[0][0]])) { - return ++$context['usedFeature']['hbhelper']; - } - - // detect block custom helpers. - if (isset($context['blockhelpers'][$vars[0][0]])) { - return ++$context['usedFeature']['bhelper']; - } - - switch ($vars[0][0]) { - case 'with': - if ($context['flags']['with']) { - if (count($vars) < 2) { - $context['error'][] = 'No argument after {{#with}} !'; - } - } else { - if (isset($vars[1][0])) { - $context['error'][] = 'Do not support {{#with var}}, you should do compile with LightnCandy::FLAG_WITH flag'; - } - } - // Continue to add usage... - case 'each': - case 'unless': - case 'if': - return ++$context['usedFeature'][$vars[0][0]]; - - default: - return ++$context['usedFeature']['sec']; - } - } - } - - /** - * Internal method used by compile(). Collect handlebars usage information, detect template error. - * - * @param string[] $token detected handlebars {{ }} token - * @param array $context current compile context - */ - protected static function scanFeatures($token, &$context) { - list($raw, $vars) = static::parseTokenArgs($token, $context); - - if (static::validateStartEnd($token, $context)) { - return; - } - - if (static::validateOperations($token, $context, $vars)) { - return; - } - - if (($token[self::POS_OP] === '^') && ($context['flags']['else'])) { - return $context['usedFeature']['else']++; - } - - if (count($vars) == 0) { - return $context['error'][] = 'Wrong variable naming in ' . static::tokenString($token); - } - - if (!isset($vars[0])) { - return static::noNamedArguments($token, $context, true, ', you should use it after a custom helper.'); - } - - if ($vars[0] !== 'else') { - $context['usedFeature'][$raw ? 'raw' : 'enc']++; - } - - // validate else and this. - switch ($vars[0][0]) { - case 'else': - if ($context['flags']['else']) { - return $context['usedFeature']['else']++; - } - break; - - case 'this': - case '.': - if ($context['level'] == 0) { - $context['usedFeature']['rootthis']++; - } - return $context['usedFeature'][($vars[0] == '.') ? 'dot' : 'this']++; - } - - // detect handlebars custom helpers. - if (isset($context['hbhelpers'][$vars[0][0]])) { - return $context['usedFeature']['hbhelper']++; - } - - // detect custom helpers. - if (isset($context['helpers'][$vars[0][0]])) { - return $context['usedFeature']['helper']++; - } - } - - /** - * Internal method used by compile(). Show error message when named arguments appear without custom helper. - * - * @param array $token detected handlebars {{ }} token - * @param array $context current compile context - * @param boolean $named is named arguments - * @param string $suggest extended hint for this no named argument error - */ - public static function noNamedArguments($token, &$context, $named, $suggest = '!') { - if ($named) { - $context['error'][] = 'Do not support name=value in ' . static::tokenString($token) . $suggest; - } - } - - /** - * Internal method used by compileToken(). Modify $token when spacing rules matched. - * - * @param array $token detected handlebars {{ }} token - * @param array $vars parsed arguments list - * @param array $context current compile context - * - * @return string|null Return compiled code segment for the token - */ - public static function handleMustacheSpacing(&$token, $vars, &$context) { - if (!$context['flags']['mustsp'] && !$context['flags']['mustpi']) { - return; - } - - // left line change detection - $lsp = preg_match('/^(.*)(\\r?\\n)([ \\t]*?)$/s', $token[self::POS_LSPACE], $lmatch); - $ind = $lsp ? $lmatch[3] : $token[self::POS_LSPACE]; - - // right line change detection - $rsp = preg_match('/^([ \\t]*?)(\\r?\\n)(.*)$/s', $token[self::POS_RSPACE], $rmatch); - $st = true; - - // setup ahead flag - $ahead = $context['tokens']['ahead']; - $context['tokens']['ahead'] = preg_match('/^[^\n]*{{/s', $token[self::POS_RSPACE] . $token[self::POS_ROTHER]); - - // reset partial indent - $context['tokens']['partialind'] = ''; - - // same tags in the same line , not standalone - if (!$lsp && $ahead) { - $st = false; - } - - // Do not need standalone detection for these tags - if (!$token[self::POS_OP] || ($token[self::POS_OP] === '&')) { - if (!$context['flags']['else'] || (isset($vars[0][0]) && ($vars[0][0] !== 'else'))) { - $st = false; - } - } - - // not standalone because other things in the same line ahead - if ($token[self::POS_LOTHER] && !$token[self::POS_LSPACE]) { - $st = false; - } - - // not standalone because other things in the same line behind - if ($token[self::POS_ROTHER] && !$token[self::POS_RSPACE]) { - $st = false; - } - - if ($st && (($lsp && $rsp) // both side cr - || ($rsp && !$token[self::POS_LOTHER]) // first line without left - || ($lsp && ($context['tokens']['current'] == $context['tokens']['count']) && !$token[self::POS_ROTHER]) // final line - )) { - // handle partial - if ($context['flags']['mustpi'] && ($token[self::POS_OP] === '>')) { - $context['tokens']['partialind'] = $ind; - } - if ($context['flags']['mustsp']) { - $token[self::POS_LSPACE] = (isset($lmatch[2]) ? ($lmatch[1] . $lmatch[2]) : ''); - $token[self::POS_RSPACE] = isset($rmatch[3]) ? $rmatch[3] : ''; - } - } - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars token. - * - * @param array $token detected handlebars {{ }} token - * @param array $context current compile context - * - * @return string Return compiled code segment for the token - */ - public static function compileToken(&$token, &$context) { - list($raw, $vars) = static::parseTokenArgs($token, $context); - $named = count(array_diff_key($vars, array_keys(array_keys($vars)))) > 0; - - // Handle spacing (standalone tags, partial indent) - static::handleMustacheSpacing($token, $vars, $context); - - // Handle space control. - if ($token[self::POS_LSPACECTL]) { - $token[self::POS_LSPACE] = ''; - } - - if ($token[self::POS_RSPACECTL]) { - $token[self::POS_RSPACE] = ''; - } - - if ($ret = static::compileSection($token, $context, $vars, $named)) { - return $ret; - } - - if ($ret = static::compileCustomHelper($context, $vars, $raw)) { - return $ret; - } - - if ($ret = static::compileElse($context, $vars)) { - return $ret; - } - - static::noNamedArguments($token, $context, $named, ', maybe you missing the custom helper?'); - - return static::compileVariable($context, $vars, $raw); - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars section token. - * - * @param array $token detected handlebars {{ }} token - * @param array $context current compile context - * @param array $vars parsed arguments list - * @param boolean $named is named arguments or not - * - * @return string|null Return compiled code segment for the token when the token is section - */ - protected static function compileSection(&$token, &$context, &$vars, $named) { - switch ($token[self::POS_OP]) { - case '>': - // mustache spec: ignore missing partial - if (!isset($context['usedPartial'][$vars[0][0]])) { - return $context['ops']['seperator']; - } - $p = array_shift($vars); - if (!isset($vars[0])) { - $vars[0] = array(); - } - $v = static::getVariableNames($vars, $context, true); - $tag = ">$p[0] " .implode(' ', $v[1]); - if ($context['flags']['runpart']) { - $sp = $context['tokens']['partialind'] ? ", '{$context['tokens']['partialind']}'" : ''; - return $context['ops']['seperator'] . static::getFuncName($context, 'p', $tag) . "\$cx, '$p[0]', $v[0]$sp){$context['ops']['seperator']}"; - } else { - if ($named || $v[0] !== 'array(array($in),array())') { - $context['error'][] = "Do not support {{{$tag}}}, you should do compile with LightnCandy::FLAG_RUNTIMEPARTIAL flag"; - } - return "{$context['ops']['seperator']}'" . static::compileTemplate($context, preg_replace('/^/m', $context['tokens']['partialind'], $context['usedPartial'][$p[0]]), $p[0]) . "'{$context['ops']['seperator']}"; - } - case '^': - // {{^}} means {{else}} - if (!$vars[0][0]) { - $vars[0][0] = 'else'; - $token[self::POS_OP] = ''; - return; - } - - // Try to compile as custom helper {{^myHelper}} - $r = static::compileBlockCustomHelper($context, $vars, true); - if ($r) { - return $r; - } - - $v = static::getVariableName($vars[0], $context); - $context['stack'][] = $v[1]; - $context['stack'][] = '^'; - static::noNamedArguments($token, $context, $named); - // Compile to inverted section {{^myVar}} - return "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'isec', '^' . $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}"; - case '/': - return static::compileBlockEnd($token, $context, $vars); - case '!': - case ' ': - return $context['ops']['seperator']; - case '#': - // Try to compile as custom helper {{#myHelper}} - $r = static::compileBlockCustomHelper($context, $vars); - if ($r) { - return $r; - } - static::noNamedArguments($token, $context, $named, ', maybe you missing the block custom helper?'); - // Compile to section {{#myVar}} - return static::compileBlockBegin($context, $vars); - } - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars block custom helper begin token. - * - * @param array $context current compile context - * @param array $vars parsed arguments list - * @param boolean $inverted the logic will be inverted - * - * @return string|null Return compiled code segment for the token - */ - protected static function compileBlockCustomHelper(&$context, $vars, $inverted = false) { - $notHBCH = !isset($context['hbhelpers'][$vars[0][0]]); - - if (!isset($context['blockhelpers'][$vars[0][0]]) && $notHBCH) { - return; - } - - $v = static::getVariableName($vars[0], $context); - $context['stack'][] = $v[1]; - $context['stack'][] = '#'; - $ch = array_shift($vars); - $inverted = $inverted ? 'true' : 'false'; - - static::addUsageCount($context, $notHBCH ? 'blockhelpers' : 'hbhelpers', $ch[0]); - $v = static::getVariableNames($vars, $context, true); - return $context['ops']['seperator'] . static::getFuncName($context, $notHBCH ? 'bch' : 'hbch', ($inverted ? '^' : '#') . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, \$in, $inverted, function(\$cx, \$in) {{$context['ops']['f_start']}"; - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars block end token. - * - * @param array $token detected handlebars {{ }} token - * @param array $context current compile context - * @param array $vars parsed arguments list - * - * @return string Return compiled code segment for the token - */ - protected static function compileBlockEnd(&$token, &$context, $vars) { - $each = false; - $pop = array_pop($context['stack']); - switch ($token[self::POS_INNERTAG]) { - case 'if': - case 'unless': - if ($pop == ':') { - array_pop($context['stack']); - return $context['usedFeature']['parent'] ? "{$context['ops']['f_end']}}){$context['ops']['seperator']}" : "{$context['ops']['cnd_end']}"; - } - return $context['usedFeature']['parent'] ? "{$context['ops']['f_end']}}){$context['ops']['seperator']}" : "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}"; - case 'with': - if ($context['flags']['with']) { - if ($pop !== 'with') { - $context['error'][] = 'Unexpect token: {{/with}} !'; - return; - } - return "{$context['ops']['f_end']}}){$context['ops']['seperator']}"; - } - break; - case 'each': - $each = true; - } - - switch($pop) { - case '#': - case '^': - $pop2 = array_pop($context['stack']); - $v = static::getVariableName($vars[0], $context); - if (!$each && ($pop2 !== $v[1])) { - $context['error'][] = 'Unexpect token ' . static::tokenString($token) . " ! Previous token {{{$pop}$pop2}} is not closed"; - return; - } - if ($pop == '^') { - return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}"; - } - return "{$context['ops']['f_end']}}){$context['ops']['seperator']}"; - default: - $context['error'][] = 'Unexpect token: ' . static::tokenString($token) . ' !'; - return; - } - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars block begin token. - * - * @param array $context current compile context - * @param array $vars parsed arguments list - * - * @return string Return compiled code segment for the token - */ - protected static function compileBlockBegin(&$context, $vars) { - $each = 'false'; - $v = isset($vars[1]) ? static::getVariableName($vars[1], $context, true) : array(null, array()); - switch ($vars[0][0]) { - case 'if': - $context['stack'][] = 'if'; - return $context['usedFeature']['parent'] - ? $context['ops']['seperator'] . static::getFuncName($context, 'ifv', 'if ' . $v[1]) . "\$cx, {$v[0]}, \$in, function(\$cx, \$in) {{$context['ops']['f_start']}" - : "{$context['ops']['cnd_start']}(" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}"; - case 'unless': - $context['stack'][] = 'unless'; - return $context['usedFeature']['parent'] - ? $context['ops']['seperator'] . static::getFuncName($context, 'unl', 'unless ' . $v[1]) . "\$cx, {$v[0]}, \$in, function(\$cx, \$in) {{$context['ops']['f_start']}" - : "{$context['ops']['cnd_start']}(!" . static::getFuncName($context, 'ifvar', $v[1]) . "\$cx, {$v[0]})){$context['ops']['cnd_then']}"; - case 'each': - $each = 'true'; - array_shift($vars); - if (!isset($vars[0])) { - $vars[0] = array(null); - } - break; - case 'with': - if ($context['flags']['with']) { - $context['stack'][] = 'with'; - return $context['ops']['seperator'] . static::getFuncName($context, 'wi', 'with ' . $v[1]) . "\$cx, {$v[0]}, \$in, function(\$cx, \$in) {{$context['ops']['f_start']}"; - } - } - - $v = static::getVariableName($vars[0], $context); - $context['stack'][] = $v[1]; - $context['stack'][] = '#'; - return $context['ops']['seperator'] . static::getFuncName($context, 'sec', (($each == 'true') ? 'each ' : '') . $v[1]) . "\$cx, {$v[0]}, \$in, $each, function(\$cx, \$in) {{$context['ops']['f_start']}"; - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars custom helper token. - * - * @param array $context current compile context - * @param array $vars parsed arguments list - * @param boolean $raw is this {{{ token or not - * @param boolean $err should cause error when missing helper or not - * - * @return string|null Return compiled code segment for the token when the token is custom helper - */ - protected static function compileCustomHelper(&$context, &$vars, $raw, $err = false) { - $notHH = !isset($context['hbhelpers'][$vars[0][0]]); - if (!isset($context['helpers'][$vars[0][0]]) && $notHH) { - if ($err) { - $context['error'][] = "Custom helper '{$vars[0][0]}' not found!"; - } - return; - } - - $fn = $raw ? 'raw' : $context['ops']['enc']; - $ch = array_shift($vars); - $v = static::getVariableNames($vars, $context, true); - static::addUsageCount($context, $notHH ? 'helpers' : 'hbhelpers', $ch[0]); - return $context['ops']['seperator'] . static::getFuncName($context, $notHH ? 'ch' : 'hbch', "$ch[0] " . implode(' ', $v[1])) . "\$cx, '$ch[0]', {$v[0]}, '$fn'" . ($notHH ? '' : ', \'$in\'') . "){$context['ops']['seperator']}"; - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars else token. - * - * @param array $context current compile context - * @param array $vars parsed arguments list - * - * @return string|null Return compiled code segment for the token when the token is else - */ - protected static function compileElse(&$context, &$vars) { - if ($vars[0][0] === 'else') { - $c = count($context['stack']) - 1; - if ($c >= 0) { - switch ($context['stack'][count($context['stack']) - 1]) { - case 'if': - case 'unless': - $context['stack'][] = ':'; - return $context['usedFeature']['parent'] ? "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}" : "{$context['ops']['cnd_else']}"; - case 'with': - case 'each': - case '#': - return "{$context['ops']['f_end']}}, function(\$cx, \$in) {{$context['ops']['f_start']}"; - } - } - $context['error'][] = '{{else}} only valid in if, unless, each, and #section context'; - } - } - - /** - * Internal method used by compile(). Return compiled PHP code partial for a handlebars variable token. - * - * @param array $context current compile context - * @param array $vars parsed arguments list - * @param boolean $raw is this {{{ token or not - * - * @return string Return compiled code segment for the token - */ - protected static function compileVariable(&$context, &$vars, $raw) { - $v = static::getVariableName($vars[0], $context); - if ($context['flags']['jsobj'] || $context['flags']['jstrue'] || $context['flags']['debug']) { - return $context['ops']['seperator'] . static::getFuncName($context, $raw ? 'raw' : $context['ops']['enc'], $v[1]) . "\$cx, {$v[0]}){$context['ops']['seperator']}"; - } else { - return $raw ? "{$context['ops']['seperator']}$v[0]{$context['ops']['seperator']}" : "{$context['ops']['seperator']}htmlentities((string){$v[0]}, ENT_QUOTES, 'UTF-8'){$context['ops']['seperator']}"; - } - } - - /** - * Internal method used by compile(). Add usage count to context - * - * @param array $context current context - * @param string $category ctegory name, can be one of: 'var', 'helpers', 'blockhelpers' - * @param string $name used name - * @param integer $count increment - * - * @expect 1 when input array('usedCount' => array('test' => array())), 'test', 'testname' - * @expect 3 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname' - * @expect 5 when input array('usedCount' => array('test' => array('testname' => 2))), 'test', 'testname', 3 - */ - protected static function addUsageCount(&$context, $category, $name, $count = 1) { - if (!isset($context['usedCount'][$category][$name])) { - $context['usedCount'][$category][$name] = 0; - } - return ($context['usedCount'][$category][$name] += $count); - } -} - -/** - * LightnCandy static class for compiled template runtime methods. - */ -class LCRun3 { - const DEBUG_ERROR_LOG = 1; - const DEBUG_ERROR_EXCEPTION = 2; - const DEBUG_TAGS = 4; - const DEBUG_TAGS_ANSI = 12; - const DEBUG_TAGS_HTML = 20; - - /** - * LightnCandy runtime method for output debug info. - * - * @param string $v expression - * @param string $f runtime function name - * @param array $cx render time context - * - * @expect '{{123}}' when input '123', 'miss', array('flags' => array('debug' => LCRun3::DEBUG_TAGS)), '' - * @expect '{{#123}}{{/123}}' when input '123', 'wi', array('flags' => array('debug' => LCRun3::DEBUG_TAGS_HTML)), false, false, function () {return 'A';} - */ - public static function debug($v, $f, $cx) { - $params = array_slice(func_get_args(), 2); - $r = call_user_func_array((isset($cx['funcs'][$f]) ? $cx['funcs'][$f] : "LCRun3::$f"), $params); - - if ($cx['flags']['debug'] & self::DEBUG_TAGS) { - $ansi = $cx['flags']['debug'] & (self::DEBUG_TAGS_ANSI - self::DEBUG_TAGS); - $html = $cx['flags']['debug'] & (self::DEBUG_TAGS_HTML - self::DEBUG_TAGS); - $cs = ($html ? (($r !== '') ? '' : '') : '') - . ($ansi ? (($r !== '') ? "\033[0;32m" : "\033[0;31m") : ''); - $ce = ($html ? '' : '') - . ($ansi ? "\033[0m" : ''); - switch ($f) { - case 'sec': - case 'ifv': - case 'unl': - case 'wi': - if ($r == '') { - if ($ansi) { - $r = "\033[0;33mSKIPPED\033[0m"; - } - if ($html) { - $r = ''; - } - } - return "$cs{{#{$v}}}$ce{$r}$cs{{/{$v}}}$ce"; - default: - return "$cs{{{$v}}}$ce"; - } - } else { - return $r; - } - } - - /** - * LightnCandy runtime method for missing data error. - * - * @param array $cx render time context - * @param string $v expression - */ - public static function miss($cx, $v) { - $e = "LCRun3: $v is not exist"; - if ($cx['flags']['debug'] & self::DEBUG_ERROR_LOG) { - error_log($e); - return; - } - if ($cx['flags']['debug'] & self::DEBUG_ERROR_EXCEPTION) { - throw new Exception($e); - } - } - - /** - * LightnCandy runtime method for variable lookup. It is slower and only be used for instance property or method detection. - * - * @param array $cx render time context - * @param array $base current variable context - * @param array $path array of names for path - * - * @return null|string Return the value or null when not found - * - * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), 0, array('a', 'b') - * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0), 'mustlok' => 0), array('a' => array('b' => 3)), array('a', 'b') - * @expect null when input array('scopes' => array(), 'flags' => array('prop' => 0, 'method' => 0, 'mustlok' => 0)), (Object) array('a' => array('b' => 3)), array('a', 'b') - * @expect 3 when input array('scopes' => array(), 'flags' => array('prop' => 1, 'method' => 0, 'mustlok' => 0)), (Object) array('a' => array('b' => 3)), array('a', 'b') - */ - public static function v($cx, $base, $path) { - $count = count($cx['scopes']); - while ($base) { - $v = $base; - foreach ($path as $name) { - if (is_array($v) && isset($v[$name])) { - $v = $v[$name]; - continue; - } - if (is_object($v)) { - if ($cx['flags']['prop'] && isset($v->$name)) { - $v = $v->$name; - continue; - } - if ($cx['flags']['method'] && is_callable(array($v, $name))) { - $v = $v->$name(); - continue; - } - } - if ($cx['flags']['mustlok']) { - unset($v); - break; - } - return null; - } - if (isset($v)) { - return $v; - } - $count--; - if ($count >= 0) { - $base = $cx['scopes'][$count]; - } else { - return null; - } - } - } - - /** - * LightnCandy runtime method for {{#if var}}. - * - * @param array $cx render time context - * @param array|string|integer|null $v value to be tested - * - * @return boolean Return true when the value is not null nor false. - * - * @expect false when input array(), null - * @expect false when input array(), 0 - * @expect false when input array(), false - * @expect true when input array(), true - * @expect true when input array(), 1 - * @expect false when input array(), '' - * @expect false when input array(), array() - * @expect true when input array(), array('') - * @expect true when input array(), array(0) - */ - public static function ifvar($cx, $v) { - return !is_null($v) && ($v !== false) && ($v !== 0) && ($v !== '') && (is_array($v) ? (count($v) > 0) : true); - } - - /** - * LightnCandy runtime method for {{#if var}} when {{../var}} used. - * - * @param array $cx render time context - * @param array|string|integer|null $v value to be tested - * @param array $in input data with current scope - * @param Closure|null $truecb callback function when test result is true - * @param Closure|null $falsecb callback function when test result is false - * - * @return string The rendered string of the section - * - * @expect '' when input array('scopes' => array()), null, array(), null - * @expect '' when input array('scopes' => array()), null, array(), function () {return 'Y';} - * @expect 'Y' when input array('scopes' => array()), 1, array(), function () {return 'Y';} - * @expect 'N' when input array('scopes' => array()), null, array(), function () {return 'Y';}, function () {return 'N';} - */ - public static function ifv($cx, $v, $in, $truecb, $falsecb = null) { - $ret = ''; - if (self::ifvar($cx, $v)) { - if ($truecb) { - $cx['scopes'][] = $in; - $ret = $truecb($cx, $in); - array_pop($cx['scopes']); - } - } else { - if ($falsecb) { - $cx['scopes'][] = $in; - $ret = $falsecb($cx, $in); - array_pop($cx['scopes']); - } - } - return $ret; - } - - /** - * LightnCandy runtime method for {{#unless var}} when {{../var}} used. - * - * @param array $cx render time context - * @param array|string|integer|null $var value be tested - * @param array|string|integer|null $in input data with current scope - * @param Closure $truecb callback function when test result is true - * @param Closure|null $falsecb callback function when test result is false - * - * @return string Return rendered string when the value is not null nor false. - * - * @expect '' when input array('scopes' => array()), null, array(), null - * @expect 'Y' when input array('scopes' => array()), null, array(), function () {return 'Y';} - * @expect '' when input array('scopes' => array()), 1, array(), function () {return 'Y';} - * @expect 'Y' when input array('scopes' => array()), null, array(), function () {return 'Y';}, function () {return 'N';} - * @expect 'N' when input array('scopes' => array()), true, array(), function () {return 'Y';}, function () {return 'N';} - */ - public static function unl($cx, $var, $in, $truecb, $falsecb = null) { - return self::ifv($cx, $var, $in, $falsecb, $truecb); - } - - /** - * LightnCandy runtime method for {{^var}} inverted section. - * - * @param array $cx render time context - * @param array|string|integer|null $v value to be tested - * - * @return boolean Return true when the value is not null nor false. - * - * @expect true when input array(), null - * @expect false when input array(), 0 - * @expect true when input array(), false - * @expect false when input array(), 'false' - * @expect true when input array(), array() - * @expect false when input array(), array('1') - */ - public static function isec($cx, $v) { - return is_null($v) || ($v === false) || (is_array($v) && (count($v) === 0)); - } - - /** - * LightnCandy runtime method for {{{var}}} . - * - * @param array $cx render time context - * @param array|string|integer|null $v value to be output - * - * @return string The raw value of the specified variable - * - * @expect true when input array('flags' => array('jstrue' => 0)), true - * @expect 'true' when input array('flags' => array('jstrue' => 1)), true - * @expect '' when input array('flags' => array('jstrue' => 0)), false - * @expect 'false' when input array('flags' => array('jstrue' => 1)), false - * @expect 'false' when input array('flags' => array('jstrue' => 1)), false, true - * @expect 'Array' when input array('flags' => array('jstrue' => 1, 'jsobj' => 0)), array('a', 'b') - * @expect 'a,b' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'b') - * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', 'c' => 'b') - * @expect '[object Object]' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('c' => 'b') - * @expect 'a,true' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a', true) - * @expect 'a,1' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1)), array('a',true) - * @expect 'a,' when input array('flags' => array('jstrue' => 0, 'jsobj' => 1)), array('a',false) - * @expect 'a,false' when input array('flags' => array('jstrue' => 1, 'jsobj' => 1)), array('a',false) - */ - public static function raw($cx, $v) { - if ($v === true) { - if ($cx['flags']['jstrue']) { - return 'true'; - } - } - - if (($v === false)) { - if ($cx['flags']['jstrue']) { - return 'false'; - } - } - - if (is_array($v)) { - if ($cx['flags']['jsobj']) { - if (count(array_diff_key($v, array_keys(array_keys($v)))) > 0) { - return '[object Object]'; - } else { - $ret = array(); - foreach ($v as $k => $vv) { - $ret[] = self::raw($cx, $vv); - } - return join(',', $ret); - } - } - } - - return "$v"; - } - - /** - * LightnCandy runtime method for {{var}} . - * - * @param array $cx render time context - * @param array|string|integer|null $var value to be htmlencoded - * - * @return string The htmlencoded value of the specified variable - * - * @expect 'a' when input array(), 'a' - * @expect 'a&b' when input array(), 'a&b' - * @expect 'a'b' when input array(), 'a\'b' - */ - public static function enc($cx, $var) { - return htmlentities(self::raw($cx, $var), ENT_QUOTES, 'UTF-8'); - } - - /** - * LightnCandy runtime method for {{var}} , and deal with single quote to same as handlebars.js . - * - * @param array $cx render time context - * @param array|string|integer|null $var value to be htmlencoded - * - * @return string The htmlencoded value of the specified variable - * - * @expect 'a' when input array(), 'a' - * @expect 'a&b' when input array(), 'a&b' - * @expect 'a'b' when input array(), 'a\'b' - * @expect '`a'b' when input array(), '`a\'b' - */ - public static function encq($cx, $var) { - return preg_replace('/`/', '`', preg_replace('/'/', ''', htmlentities(self::raw($cx, $var), ENT_QUOTES, 'UTF-8'))); - } - - /** - * LightnCandy runtime method for {{#var}} section. - * - * @param array $cx render time context - * @param array|string|integer|null $v value for the section - * @param array|string|integer|null $in input data with current scope - * @param boolean $each true when rendering #each - * @param Closure $cb callback function to render child context - * @param Closure|null $else callback function to render child context when {{else}} - * - * @return string The rendered string of the section - * - * @expect '' when input array('flags' => array('spvar' => 0)), false, false, false, function () {return 'A';} - * @expect '' when input array('flags' => array('spvar' => 0)), null, null, false, function () {return 'A';} - * @expect 'A' when input array('flags' => array('spvar' => 0)), true, true, false, function () {return 'A';} - * @expect 'A' when input array('flags' => array('spvar' => 0)), 0, 0, false, function () {return 'A';} - * @expect '-a=' when input array('flags' => array('spvar' => 0)), array('a'), array('a'), false, function ($c, $i) {return "-$i=";} - * @expect '-a=-b=' when input array('flags' => array('spvar' => 0)), array('a','b'), array('a','b'), false, function ($c, $i) {return "-$i=";} - * @expect '' when input array('flags' => array('spvar' => 0)), 'abc', 'abc', true, function ($c, $i) {return "-$i=";} - * @expect '-b=' when input array('flags' => array('spvar' => 0, 'mustsec' => 0)), array('a' => 'b'), array('a' => 'b'), true, function ($c, $i) {return "-$i=";} - * @expect '1' when input array('flags' => array('spvar' => 0)), 'b', 'b', false, function ($c, $i) {return count($i);} - * @expect '1' when input array('flags' => array('spvar' => 0)), 1, 1, false, function ($c, $i) {return print_r($i, true);} - * @expect '0' when input array('flags' => array('spvar' => 0)), 0, 0, false, function ($c, $i) {return print_r($i, true);} - * @expect '{"b":"c"}' when input array('flags' => array('spvar' => 0, 'mustsec' => 0)), array('b' => 'c'), array('b' => 'c'), false, function ($c, $i) {return json_encode($i);} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), array(), 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), array(), 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), false, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), false, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), '', 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'cb' when input array('flags' => array('spvar' => 0)), '', 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), 0, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'cb' when input array('flags' => array('spvar' => 0)), 0, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'inv' when input array('flags' => array('spvar' => 0)), new stdClass, 0, true, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect 'cb' when input array('flags' => array('spvar' => 0)), new stdClass, 0, false, function ($c, $i) {return 'cb';}, function ($c, $i) {return 'inv';} - * @expect '268' when input array('flags' => array('spvar' => 1)), array(1,3,4), 0, false, function ($c, $i) {return $i * 2;} - * @expect '038' when input array('flags' => array('spvar' => 1), 'sp_vars'=>array()), array(1,3,'a'=>4), 0, true, function ($c, $i) {return $i * $c['sp_vars']['index'];} - */ - public static function sec($cx, $v, $in, $each, $cb, $else = null) { - $isAry = is_array($v); - $isTrav = $v instanceof Traversable; - $loop = $each; - $keys = null; - $last = null; - $isObj = false; - - if ($isAry && $else !== null && count($v) === 0) { - return $else($cx, $in); - } - - // #var, detect input type is object or not - if (!$loop && $isAry) { - $keys = array_keys($v); - $loop = (count(array_diff_key($v, array_keys($keys))) == 0); - $isObj = !$loop; - } - - if (($loop && $isAry) || $isTrav) { - if ($each && !$isTrav) { - // Detect input type is object or not when never done once - if ($keys == null) { - $keys = array_keys($v); - $isObj = (count(array_diff_key($v, array_keys($keys))) > 0); - } - } - $ret = array(); - $cx['scopes'][] = $in; - $i = 0; - if ($cx['flags']['spvar']) { - $old_spvar = $cx['sp_vars']; - $cx['sp_vars'] = array( - '_parent' => $old_spvar, - 'root' => $old_spvar['root'], - ); - if (!$isTrav) { - $last = count($keys) - 1; - } - } - foreach ($v as $index => $raw) { - if ($cx['flags']['spvar']) { - $cx['sp_vars']['first'] = ($i === 0); - $cx['sp_vars']['last'] = ($i == $last); - $cx['sp_vars']['key'] = $index; - $cx['sp_vars']['index'] = $i; - $i++; - } - $ret[] = $cb($cx, $raw); - } - if ($cx['flags']['spvar']) { - if ($isObj) { - unset($cx['sp_vars']['key']); - } else { - unset($cx['sp_vars']['last']); - } - unset($cx['sp_vars']['index']); - unset($cx['sp_vars']['first']); - $cx['sp_vars'] = $old_spvar; - } - array_pop($cx['scopes']); - return join('', $ret); - } - if ($each) { - if ($else !== null) { - $cx['scopes'][] = $in; - $ret = $else($cx, $v); - array_pop($cx['scopes']); - return $ret; - } - return ''; - } - if ($isAry) { - if ($cx['flags']['mustsec']) { - $cx['scopes'][] = $v; - } - $ret = $cb($cx, $v); - if ($cx['flags']['mustsec']) { - array_pop($cx['scopes']); - } - return $ret; - } - - if ($v === true) { - return $cb($cx, $in); - } - - if (!is_null($v) && ($v !== false)) { - return $cb($cx, $v); - } - - if ($else !== null) { - return $else($cx, $in); - } - - return ''; - } - - /** - * LightnCandy runtime method for {{#with var}} . - * - * @param array $cx render time context - * @param array|string|integer|null $v value to be the new context - * @param array|string|integer|null $in input data with current scope - * @param Closure $cb callback function to render child context - * @param Closure|null $else callback function to render child context when {{else}} - * - * @return string The rendered string of the token - * - * @expect '' when input array(), false, false, function () {return 'A';} - * @expect '' when input array(), null, null, function () {return 'A';} - * @expect '-Array=' when input array(), array('a'=>'b'), array('a' => 'b'), function ($c, $i) {return "-$i=";} - * @expect '-b=' when input array(), 'b', array('a' => 'b'), function ($c, $i) {return "-$i=";} - */ - public static function wi($cx, $v, $in, $cb, $else = null) { - if (($v === false) || ($v === null)) { - return $else ? $else($cx, $in) : ''; - } - $cx['scopes'][] = $in; - $ret = $cb($cx, $v); - array_pop($cx['scopes']); - return $ret; - } - - /** - * LightnCandy runtime method for {{> partial}} . - * - * @param array $cx render time context - * @param string $p partial name - * @param array|string|integer|null $v value to be the new context - * - * @return string The rendered string of the partial - * - */ - public static function p($cx, $p, $v, $sp = '') { - return call_user_func($cx['partials'][$p], $cx, is_array($v[0][0]) ? array_merge($v[0][0], $v[1]) : $v[0][0], $sp); - } - - /** - * LightnCandy runtime method for custom helpers. - * - * @param array $cx render time context - * @param string $ch the name of custom helper to be executed - * @param array $vars variables for the helper - * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'. - * - * @return string The rendered string of the token - * - * @expect '=-=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('-'),array()), 'raw' - * @expect '=&=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('&'),array()), 'enc' - * @expect '='=' when input array('helpers' => array('a' => function ($i) {return "=$i[0]=";})), 'a', array(array('\''),array()), 'encq' - * @expect '=b=' when input array('helpers' => array('a' => function ($i,$j) {return "={$j['a']}=";})), 'a', array(array(),array('a' => 'b')), 'raw' - */ - public static function ch($cx, $ch, $vars, $op) { - return self::chret(call_user_func_array($cx['helpers'][$ch], $vars), $op); - } - - /** - * LightnCandy runtime method to handle response of custom helpers. - * - * @param string|array $ret return value from custom helper - * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'. - * - * @return string The rendered string of the token - * - * @expect '=&=' when input '=&=', 'raw' - * @expect '=&'=' when input '=&\'=', 'enc' - * @expect '=&'=' when input '=&\'=', 'encq' - * @expect '=&'=' when input array('=&\'='), 'enc' - * @expect '=&'=' when input array('=&\'='), 'encq' - * @expect '=&=' when input array('=&=', false), 'enc' - * @expect '=&=' when input array('=&=', false), 'raw' - * @expect '=&=' when input array('=&=', 'raw'), 'enc' - * @expect '=&'=' when input array('=&\'=', 'encq'), 'raw' - */ - public static function chret($ret, $op) { - if (is_array($ret)) { - if (isset($ret[1]) && $ret[1]) { - $op = $ret[1]; - } - $ret = $ret[0]; - } - - switch ($op) { - case 'enc': - return htmlentities($ret, ENT_QUOTES, 'UTF-8'); - case 'encq': - return preg_replace('/'/', ''', htmlentities($ret, ENT_QUOTES, 'UTF-8')); - } - return $ret; - } - - /** - * LightnCandy runtime method for Handlebars.js style custom helpers. - * - * @param array $cx render time context - * @param string $ch the name of custom helper to be executed - * @param array|string|integer|null $vars variables for the helper - * @param string $op the name of variable resolver. should be one of: 'raw', 'enc', or 'encq'. - * @param boolean $inverted the logic will be inverted - * @param Closure $cb callback function to render child context - * @param Closure|null $else callback function to render child context when {{else}} - * - * @return string The rendered string of the token - */ - public static function hbch($cx, $ch, $vars, $op, $inverted, $cb = false, $else = false) { - $isBlock = (is_object($cb) && ($cb instanceof Closure)); - $args = $vars[0]; - $options = array( - 'name' => $ch, - 'hash' => $vars[1] - ); - - // $invert the logic - if ($inverted) { - $tmp = $else; - $else = $cb; - $cb = $tmp; - } - - if ($isBlock) { - $options['fn'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $cb) { - if ($cx['flags']['echo']) { - ob_start(); - } - if ($context === '_NO_INPUT_HERE_') { - $ret = $cb($cx, $op); - } else { - $cx['scopes'][] = $op; - $ret = $cb($cx, $context); - array_pop($cx['scopes']); - } - return $cx['flags']['echo'] ? ob_get_clean() : $ret; - }; - } - - if ($else) { - $options['inverse'] = function ($context = '_NO_INPUT_HERE_') use ($cx, $op, $else) { - if ($cx['flags']['echo']) { - ob_start(); - } - if ($context === '_NO_INPUT_HERE_') { - $ret = $else($cx, $op); - } else { - $cx['scopes'][] = $op; - $ret = $else($cx, $context); - array_pop($cx['scopes']); - } - return $cx['flags']['echo'] ? ob_get_clean() : $ret; - }; - } - - // prepare $options['data'] - if ($cx['flags']['spvar']) { - $options['data'] = $cx['sp_vars']; - $options['data']['root'] = $cx['scopes'][0]; - } - - $args[] = $options; - $e = null; - $r = true; - - try { - $r = call_user_func_array($cx['hbhelpers'][$ch], $args); - } catch (Exception $E) { - $e = "LCRun3: call custom helper '$ch' error: " . $E->getMessage(); - } - - if ($r === false) { - if ($e === null) { - $e = "LCRun3: call custom helper '$ch' error"; - } - } - - if($e !== null) { - if ($cx['flags']['debug'] & self::DEBUG_ERROR_LOG) { - error_log($e); - } - if ($cx['flags']['debug'] & self::DEBUG_ERROR_EXCEPTION) { - throw new Exception($e); - } - } - - return self::chret($r, $isBlock ? 'raw' : $op); - } - - /** - * LightnCandy runtime method for block custom helpers. - * - * @param array $cx render time context - * @param string $ch the name of custom helper to be executed - * @param array|string|integer|null $vars variables for the helper - * @param array|string|integer|null $in input data with current scope - * @param boolean $inverted the logic will be inverted - * @param Closure $cb callback function to render child context - * @param Closure|null $else callback function to render child context when {{else}} - * - * @return string The rendered string of the token - * - * @expect '4.2.3' when input array('blockhelpers' => array('a' => function ($cx) {return array($cx,2,3);})), 'a', array(0, 0), 4, false, function($cx, $i) {return implode('.', $i);} - * @expect '2.6.5' when input array('blockhelpers' => array('a' => function ($cx,$in) {return array($cx,$in[0],5);})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);} - * @expect '' when input array('blockhelpers' => array('a' => function ($cx,$in) {})), 'a', array('6', 0), 2, false, function($cx, $i) {return implode('.', $i);} - */ - public static function bch($cx, $ch, $vars, $in, $inverted, $cb, $else = false) { - $r = call_user_func($cx['blockhelpers'][$ch], $in, $vars[0], $vars[1]); - - // $invert the logic - if ($inverted) { - $tmp = $else; - $else = $cb; - $cb = $tmp; - } - - $ret = ''; - if (is_null($r)) { - if ($else) { - $cx['scopes'][] = $in; - $ret = $else($cx, $r); - array_pop($cx['scopes']); - } - } else { - if ($cb) { - $cx['scopes'][] = $in; - $ret = $cb($cx, $r); - array_pop($cx['scopes']); - } - } - - return $ret; - } -} - -?> \ No newline at end of file