From c22309fd51c3191dda10122c2b7c0f250f96c2c5 Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 14 Feb 2014 14:29:30 +0000 Subject: [PATCH] ApiGen + woodocs template + grunt task --- .gitignore | 1 + Gruntfile.js | 13 +- apigen/ApiGen/Backend.php | 301 ++ apigen/ApiGen/Config.php | 602 +++ apigen/ApiGen/ConfigException.php | 25 + apigen/ApiGen/Generator.php | 2121 ++++++++++ apigen/ApiGen/IHelperSet.php | 28 + apigen/ApiGen/MacroSet.php | 23 + apigen/ApiGen/ReflectionBase.php | 240 ++ apigen/ApiGen/ReflectionClass.php | 1423 +++++++ apigen/ApiGen/ReflectionConstant.php | 145 + apigen/ApiGen/ReflectionElement.php | 373 ++ apigen/ApiGen/ReflectionExtension.php | 135 + apigen/ApiGen/ReflectionFunction.php | 64 + apigen/ApiGen/ReflectionFunctionBase.php | 151 + apigen/ApiGen/ReflectionMethod.php | 250 ++ apigen/ApiGen/ReflectionMethodMagic.php | 729 ++++ apigen/ApiGen/ReflectionParameter.php | 215 + apigen/ApiGen/ReflectionParameterMagic.php | 479 +++ apigen/ApiGen/ReflectionProperty.php | 214 + apigen/ApiGen/ReflectionPropertyMagic.php | 651 +++ apigen/ApiGen/SourceFilesFilterIterator.php | 75 + apigen/ApiGen/Template.php | 809 ++++ apigen/ApiGen/Tree.php | 90 + apigen/CHANGELOG.md | 128 + apigen/LICENSE.md | 32 + apigen/README.md | 285 ++ apigen/apigen.bat | 16 + apigen/apigen.neon.example | 67 + apigen/apigen.php | 254 ++ apigen/libs/FSHL/FSHL/Generator.php | 624 +++ apigen/libs/FSHL/FSHL/Highlighter.php | 482 +++ apigen/libs/FSHL/FSHL/Lexer.php | 66 + apigen/libs/FSHL/FSHL/Lexer/Cache/Cpp.php | 543 +++ apigen/libs/FSHL/FSHL/Lexer/Cache/Css.php | 794 ++++ apigen/libs/FSHL/FSHL/Lexer/Cache/Html.php | 672 +++ .../libs/FSHL/FSHL/Lexer/Cache/HtmlOnly.php | 706 ++++ apigen/libs/FSHL/FSHL/Lexer/Cache/Java.php | 479 +++ .../libs/FSHL/FSHL/Lexer/Cache/Javascript.php | 507 +++ apigen/libs/FSHL/FSHL/Lexer/Cache/Minimal.php | 162 + apigen/libs/FSHL/FSHL/Lexer/Cache/Neon.php | 710 ++++ apigen/libs/FSHL/FSHL/Lexer/Cache/Php.php | 992 +++++ apigen/libs/FSHL/FSHL/Lexer/Cache/Python.php | 711 ++++ apigen/libs/FSHL/FSHL/Lexer/Cache/Sql.php | 594 +++ apigen/libs/FSHL/FSHL/Lexer/Cache/Texy.php | 972 +++++ apigen/libs/FSHL/FSHL/Lexer/Cpp.php | 308 ++ apigen/libs/FSHL/FSHL/Lexer/Css.php | 255 ++ apigen/libs/FSHL/FSHL/Lexer/Html.php | 218 + apigen/libs/FSHL/FSHL/Lexer/HtmlOnly.php | 220 + apigen/libs/FSHL/FSHL/Lexer/Java.php | 221 + apigen/libs/FSHL/FSHL/Lexer/Javascript.php | 260 ++ apigen/libs/FSHL/FSHL/Lexer/Minimal.php | 92 + apigen/libs/FSHL/FSHL/Lexer/Neon.php | 230 ++ apigen/libs/FSHL/FSHL/Lexer/Php.php | 3655 +++++++++++++++++ apigen/libs/FSHL/FSHL/Lexer/Python.php | 502 +++ apigen/libs/FSHL/FSHL/Lexer/Sql.php | 557 +++ apigen/libs/FSHL/FSHL/Lexer/Texy.php | 297 ++ apigen/libs/FSHL/FSHL/Output.php | 48 + apigen/libs/FSHL/FSHL/Output/Html.php | 89 + apigen/libs/FSHL/FSHL/Output/HtmlManual.php | 123 + apigen/libs/FSHL/LICENSE | 19 + .../Nette/Nette/Application/Application.php | 262 ++ .../Application/Diagnostics/RoutingPanel.php | 125 + .../templates/RoutingPanel.panel.phtml | 119 + .../templates/RoutingPanel.tab.phtml | 10 + .../Nette/Nette/Application/IPresenter.php | 32 + .../Nette/Application/IPresenterFactory.php | 40 + .../Nette/Nette/Application/IResponse.php | 32 + .../libs/Nette/Nette/Application/IRouter.php | 46 + .../Nette/Application/MicroPresenter.php | 164 + .../Nette/Application/PresenterFactory.php | 171 + .../libs/Nette/Nette/Application/Request.php | 270 ++ .../Application/Responses/FileResponse.php | 139 + .../Application/Responses/ForwardResponse.php | 60 + .../Application/Responses/JsonResponse.php | 83 + .../Responses/RedirectResponse.php | 78 + .../Application/Responses/TextResponse.php | 66 + .../Nette/Application/Routers/CliRouter.php | 129 + .../Nette/Nette/Application/Routers/Route.php | 825 ++++ .../Nette/Application/Routers/RouteList.php | 146 + .../Application/Routers/SimpleRouter.php | 163 + .../Application/UI/BadSignalException.php | 28 + .../Nette/Nette/Application/UI/Control.php | 225 + .../libs/Nette/Nette/Application/UI/Form.php | 147 + .../Nette/Application/UI/IRenderable.php | 38 + .../Nette/Application/UI/ISignalReceiver.php | 32 + .../Nette/Application/UI/IStatePersistent.php | 40 + .../Application/UI/InvalidLinkException.php | 36 + .../libs/Nette/Nette/Application/UI/Link.php | 117 + .../Nette/Nette/Application/UI/Multiplier.php | 42 + .../Nette/Nette/Application/UI/Presenter.php | 1446 +++++++ .../Application/UI/PresenterComponent.php | 450 ++ .../UI/PresenterComponentReflection.php | 174 + .../Nette/Nette/Application/exceptions.php | 92 + .../Nette/Application/templates/error.phtml | 37 + apigen/libs/Nette/Nette/Caching/Cache.php | 428 ++ apigen/libs/Nette/Nette/Caching/IStorage.php | 63 + .../libs/Nette/Nette/Caching/OutputHelper.php | 59 + .../Nette/Caching/Storages/DevNullStorage.php | 81 + .../Nette/Caching/Storages/FileJournal.php | 1196 ++++++ .../Nette/Caching/Storages/FileStorage.php | 429 ++ .../Nette/Nette/Caching/Storages/IJournal.php | 42 + .../Caching/Storages/MemcachedStorage.php | 207 + .../Nette/Caching/Storages/MemoryStorage.php | 91 + .../Nette/Caching/Storages/PhpFileStorage.php | 59 + .../Nette/Nette/ComponentModel/Component.php | 352 ++ .../Nette/Nette/ComponentModel/Container.php | 258 ++ .../Nette/Nette/ComponentModel/IComponent.php | 47 + .../Nette/Nette/ComponentModel/IContainer.php | 56 + .../RecursiveComponentIterator.php | 58 + .../Nette/Config/Adapters/IniAdapter.php | 151 + .../Nette/Config/Adapters/NeonAdapter.php | 93 + .../Nette/Config/Adapters/PhpAdapter.php | 48 + apigen/libs/Nette/Nette/Config/Compiler.php | 367 ++ .../Nette/Nette/Config/CompilerExtension.php | 130 + .../libs/Nette/Nette/Config/Configurator.php | 343 ++ .../Config/Extensions/ConstantsExtension.php | 34 + .../Config/Extensions/NetteExtension.php | 371 ++ .../Nette/Config/Extensions/PhpExtension.php | 55 + apigen/libs/Nette/Nette/Config/Helpers.php | 95 + apigen/libs/Nette/Nette/Config/IAdapter.php | 40 + apigen/libs/Nette/Nette/Config/Loader.php | 139 + apigen/libs/Nette/Nette/DI/Container.php | 371 ++ .../libs/Nette/Nette/DI/ContainerBuilder.php | 585 +++ .../Nette/DI/Diagnostics/ContainerPanel.php | 93 + .../templates/ContainerPanel.panel.phtml | 99 + .../templates/ContainerPanel.tab.phtml | 9 + apigen/libs/Nette/Nette/DI/Helpers.php | 160 + apigen/libs/Nette/Nette/DI/IContainer.php | 52 + apigen/libs/Nette/Nette/DI/NestedAccessor.php | 101 + .../libs/Nette/Nette/DI/ServiceDefinition.php | 134 + apigen/libs/Nette/Nette/DI/Statement.php | 39 + apigen/libs/Nette/Nette/DI/exceptions.php | 32 + .../libs/Nette/Nette/Database/Connection.php | 296 ++ .../Database/Diagnostics/ConnectionPanel.php | 152 + .../Nette/Database/Drivers/MsSqlDriver.php | 152 + .../Nette/Database/Drivers/MySqlDriver.php | 223 + .../Nette/Database/Drivers/OciDriver.php | 165 + .../Nette/Database/Drivers/OdbcDriver.php | 151 + .../Nette/Database/Drivers/PgSqlDriver.php | 227 + .../Nette/Database/Drivers/Sqlite2Driver.php | 68 + .../Nette/Database/Drivers/SqliteDriver.php | 232 ++ apigen/libs/Nette/Nette/Database/Helpers.php | 173 + .../libs/Nette/Nette/Database/IReflection.php | 69 + .../Nette/Database/ISupplementalDriver.php | 97 + .../Reflection/ConventionalReflection.php | 96 + .../Reflection/DiscoveredReflection.php | 194 + apigen/libs/Nette/Nette/Database/Row.php | 47 + .../libs/Nette/Nette/Database/SqlLiteral.php | 44 + .../Nette/Nette/Database/SqlPreprocessor.php | 163 + .../libs/Nette/Nette/Database/Statement.php | 221 + .../Nette/Nette/Database/Table/ActiveRow.php | 316 ++ .../Nette/Database/Table/GroupedSelection.php | 257 ++ .../Nette/Nette/Database/Table/Selection.php | 787 ++++ .../Nette/Nette/Database/Table/SqlBuilder.php | 383 ++ apigen/libs/Nette/Nette/Diagnostics/Bar.php | 79 + .../Nette/Nette/Diagnostics/BlueScreen.php | 139 + .../libs/Nette/Nette/Diagnostics/Debugger.php | 694 ++++ .../Nette/Diagnostics/DefaultBarPanel.php | 76 + .../Nette/Nette/Diagnostics/FireLogger.php | 188 + .../libs/Nette/Nette/Diagnostics/Helpers.php | 233 ++ .../Nette/Nette/Diagnostics/IBarPanel.php | 38 + .../libs/Nette/Nette/Diagnostics/Logger.php | 106 + .../Nette/Nette/Diagnostics/shortcuts.php | 25 + .../templates/bar.dumps.panel.phtml | 64 + .../Diagnostics/templates/bar.dumps.tab.phtml | 14 + .../templates/bar.errors.panel.phtml | 26 + .../templates/bar.errors.tab.phtml | 15 + .../templates/bar.memory.tab.phtml | 15 + .../Nette/Diagnostics/templates/bar.phtml | 616 +++ .../Diagnostics/templates/bar.time.tab.phtml | 15 + .../Diagnostics/templates/bluescreen.phtml | 644 +++ .../Nette/Diagnostics/templates/error.phtml | 26 + .../Nette/Diagnostics/templates/netteQ.js | 332 ++ apigen/libs/Nette/Nette/Forms/Container.php | 493 +++ .../libs/Nette/Nette/Forms/ControlGroup.php | 124 + .../Nette/Forms/Controls/BaseControl.php | 666 +++ .../Nette/Nette/Forms/Controls/Button.php | 60 + .../Nette/Nette/Forms/Controls/Checkbox.php | 60 + .../Nette/Forms/Controls/HiddenField.php | 75 + .../Nette/Forms/Controls/ImageButton.php | 63 + .../Nette/Forms/Controls/MultiSelectBox.php | 110 + .../Nette/Nette/Forms/Controls/RadioList.php | 190 + .../Nette/Nette/Forms/Controls/SelectBox.php | 242 ++ .../Nette/Forms/Controls/SubmitButton.php | 123 + .../Nette/Nette/Forms/Controls/TextArea.php | 53 + .../Nette/Nette/Forms/Controls/TextBase.php | 265 ++ .../Nette/Nette/Forms/Controls/TextInput.php | 104 + .../Nette/Forms/Controls/UploadControl.php | 138 + apigen/libs/Nette/Nette/Forms/Form.php | 644 +++ apigen/libs/Nette/Nette/Forms/IControl.php | 70 + .../libs/Nette/Nette/Forms/IFormRenderer.php | 33 + .../Nette/Nette/Forms/ISubmitterControl.php | 38 + .../Forms/Rendering/DefaultFormRenderer.php | 514 +++ apigen/libs/Nette/Nette/Forms/Rule.php | 55 + apigen/libs/Nette/Nette/Forms/Rules.php | 275 ++ apigen/libs/Nette/Nette/Http/Context.php | 114 + apigen/libs/Nette/Nette/Http/FileUpload.php | 222 + apigen/libs/Nette/Nette/Http/IRequest.php | 140 + apigen/libs/Nette/Nette/Http/IResponse.php | 139 + .../libs/Nette/Nette/Http/ISessionStorage.php | 38 + apigen/libs/Nette/Nette/Http/Request.php | 371 ++ .../libs/Nette/Nette/Http/RequestFactory.php | 252 ++ apigen/libs/Nette/Nette/Http/Response.php | 339 ++ apigen/libs/Nette/Nette/Http/Session.php | 591 +++ .../libs/Nette/Nette/Http/SessionSection.php | 273 ++ apigen/libs/Nette/Nette/Http/Url.php | 526 +++ apigen/libs/Nette/Nette/Http/UrlScript.php | 89 + apigen/libs/Nette/Nette/Http/UserStorage.php | 221 + .../Nette/Nette/Iterators/CachingIterator.php | 266 ++ apigen/libs/Nette/Nette/Iterators/Filter.php | 47 + .../Nette/Nette/Iterators/InstanceFilter.php | 62 + apigen/libs/Nette/Nette/Iterators/Mapper.php | 47 + .../Nette/Nette/Iterators/RecursiveFilter.php | 66 + .../libs/Nette/Nette/Iterators/Recursor.php | 60 + apigen/libs/Nette/Nette/Latte/Compiler.php | 530 +++ apigen/libs/Nette/Nette/Latte/Engine.php | 77 + apigen/libs/Nette/Nette/Latte/HtmlNode.php | 53 + apigen/libs/Nette/Nette/Latte/IMacro.php | 50 + apigen/libs/Nette/Nette/Latte/MacroNode.php | 98 + .../libs/Nette/Nette/Latte/MacroTokenizer.php | 69 + .../Nette/Nette/Latte/Macros/CacheMacro.php | 132 + .../Nette/Nette/Latte/Macros/CoreMacros.php | 415 ++ .../Nette/Nette/Latte/Macros/FormMacros.php | 137 + .../Nette/Nette/Latte/Macros/MacroSet.php | 143 + .../Nette/Nette/Latte/Macros/UIMacros.php | 520 +++ apigen/libs/Nette/Nette/Latte/Parser.php | 405 ++ apigen/libs/Nette/Nette/Latte/PhpWriter.php | 319 ++ apigen/libs/Nette/Nette/Latte/Token.php | 53 + apigen/libs/Nette/Nette/Latte/exceptions.php | 28 + .../libs/Nette/Nette/Loaders/AutoLoader.php | 95 + .../libs/Nette/Nette/Loaders/NetteLoader.php | 120 + .../libs/Nette/Nette/Loaders/RobotLoader.php | 407 ++ .../Nette/Nette/Localization/ITranslator.php | 34 + apigen/libs/Nette/Nette/Mail/IMailer.php | 33 + apigen/libs/Nette/Nette/Mail/Message.php | 506 +++ apigen/libs/Nette/Nette/Mail/MimePart.php | 392 ++ .../libs/Nette/Nette/Mail/SendmailMailer.php | 63 + apigen/libs/Nette/Nette/Mail/SmtpMailer.php | 200 + .../Nette/Nette/Reflection/Annotation.php | 44 + .../Nette/Reflection/AnnotationsParser.php | 357 ++ .../libs/Nette/Nette/Reflection/ClassType.php | 360 ++ .../libs/Nette/Nette/Reflection/Extension.php | 106 + .../Nette/Nette/Reflection/GlobalFunction.php | 154 + .../Nette/Nette/Reflection/IAnnotation.php | 28 + apigen/libs/Nette/Nette/Reflection/Method.php | 236 ++ .../libs/Nette/Nette/Reflection/Parameter.php | 151 + .../libs/Nette/Nette/Reflection/Property.php | 160 + .../Security/AuthenticationException.php | 25 + .../Nette/Security/Diagnostics/UserPanel.php | 62 + .../templates/UserPanel.panel.phtml | 24 + .../Diagnostics/templates/UserPanel.tab.phtml | 12 + .../Nette/Nette/Security/IAuthenticator.php | 44 + .../Nette/Nette/Security/IAuthorizator.php | 45 + .../libs/Nette/Nette/Security/IIdentity.php | 38 + .../libs/Nette/Nette/Security/IResource.php | 32 + apigen/libs/Nette/Nette/Security/IRole.php | 32 + .../Nette/Nette/Security/IUserStorage.php | 72 + apigen/libs/Nette/Nette/Security/Identity.php | 175 + .../libs/Nette/Nette/Security/Permission.php | 843 ++++ .../Nette/Security/SimpleAuthenticator.php | 61 + apigen/libs/Nette/Nette/Security/User.php | 318 ++ .../Nette/Nette/Templating/FileTemplate.php | 125 + .../Nette/Templating/FilterException.php | 48 + .../libs/Nette/Nette/Templating/Helpers.php | 363 ++ .../Nette/Nette/Templating/IFileTemplate.php | 39 + .../libs/Nette/Nette/Templating/ITemplate.php | 32 + .../libs/Nette/Nette/Templating/Template.php | 474 +++ apigen/libs/Nette/Nette/Utils/Arrays.php | 195 + apigen/libs/Nette/Nette/Utils/Finder.php | 420 ++ apigen/libs/Nette/Nette/Utils/Html.php | 614 +++ apigen/libs/Nette/Nette/Utils/Json.php | 96 + .../libs/Nette/Nette/Utils/LimitedScope.php | 76 + .../Nette/Nette/Utils/MimeTypeDetector.php | 87 + apigen/libs/Nette/Nette/Utils/Neon.php | 373 ++ apigen/libs/Nette/Nette/Utils/Paginator.php | 250 ++ .../Nette/Utils/PhpGenerator/ClassType.php | 145 + .../Nette/Utils/PhpGenerator/Helpers.php | 201 + .../Nette/Nette/Utils/PhpGenerator/Method.php | 139 + .../Nette/Utils/PhpGenerator/Parameter.php | 52 + .../Nette/Utils/PhpGenerator/PhpLiteral.php | 34 + .../Nette/Utils/PhpGenerator/Property.php | 52 + apigen/libs/Nette/Nette/Utils/SafeStream.php | 309 ++ apigen/libs/Nette/Nette/Utils/Strings.php | 554 +++ apigen/libs/Nette/Nette/Utils/Tokenizer.php | 301 ++ apigen/libs/Nette/Nette/Utils/Validators.php | 288 ++ apigen/libs/Nette/Nette/common/ArrayHash.php | 118 + apigen/libs/Nette/Nette/common/ArrayList.php | 117 + apigen/libs/Nette/Nette/common/Callback.php | 194 + apigen/libs/Nette/Nette/common/DateTime.php | 131 + .../libs/Nette/Nette/common/Environment.php | 360 ++ apigen/libs/Nette/Nette/common/Framework.php | 44 + .../Nette/Nette/common/FreezableObject.php | 76 + apigen/libs/Nette/Nette/common/IFreezable.php | 38 + apigen/libs/Nette/Nette/common/Image.php | 659 +++ apigen/libs/Nette/Nette/common/Object.php | 173 + .../libs/Nette/Nette/common/ObjectMixin.php | 247 ++ apigen/libs/Nette/Nette/common/exceptions.php | 186 + apigen/libs/Nette/Nette/loader.php | 75 + apigen/libs/Nette/license.txt | 62 + apigen/libs/Texy/license.txt | 339 ++ apigen/libs/Texy/texy/libs/DTD.php | 466 +++ .../libs/Texy/texy/libs/RegExp.Patterns.php | 51 + .../libs/Texy/texy/libs/TexyConfigurator.php | 114 + .../Texy/texy/libs/TexyHandlerInvocation.php | 105 + apigen/libs/Texy/texy/libs/TexyHtml.php | 604 +++ apigen/libs/Texy/texy/libs/TexyModifier.php | 219 + apigen/libs/Texy/texy/libs/TexyModule.php | 25 + apigen/libs/Texy/texy/libs/TexyObject.php | 335 ++ apigen/libs/Texy/texy/libs/TexyParser.php | 349 ++ apigen/libs/Texy/texy/libs/TexyUtf.php | 145 + .../Texy/texy/modules/TexyBlockModule.php | 230 ++ .../texy/modules/TexyBlockQuoteModule.php | 131 + .../Texy/texy/modules/TexyEmoticonModule.php | 135 + .../Texy/texy/modules/TexyFigureModule.php | 136 + .../Texy/texy/modules/TexyHeadingModule.php | 250 ++ .../Texy/texy/modules/TexyHorizLineModule.php | 86 + .../libs/Texy/texy/modules/TexyHtmlModule.php | 281 ++ .../texy/modules/TexyHtmlOutputModule.php | 311 ++ .../Texy/texy/modules/TexyImageModule.php | 372 ++ .../libs/Texy/texy/modules/TexyLinkModule.php | 493 +++ .../libs/Texy/texy/modules/TexyListModule.php | 255 ++ .../Texy/texy/modules/TexyLongWordsModule.php | 201 + .../Texy/texy/modules/TexyParagraphModule.php | 136 + .../Texy/texy/modules/TexyPhraseModule.php | 334 ++ .../Texy/texy/modules/TexyScriptModule.php | 119 + .../Texy/texy/modules/TexyTableModule.php | 309 ++ .../texy/modules/TexyTypographyModule.php | 139 + apigen/libs/Texy/texy/netterobots.txt | 1 + apigen/libs/Texy/texy/texy.php | 965 +++++ apigen/libs/TokenReflection/LICENSE | 28 + .../TokenReflection/Broker.php | 542 +++ .../TokenReflection/Broker/Backend.php | 212 + .../TokenReflection/Broker/Backend/Memory.php | 574 +++ .../TokenReflection/Dummy/ReflectionClass.php | 1091 +++++ .../Exception/BaseException.php | 102 + .../Exception/BrokerException.php | 66 + .../Exception/FileProcessingException.php | 80 + .../Exception/ParseException.php | 264 ++ .../Exception/RuntimeException.php | 72 + .../Exception/StreamException.php | 96 + .../TokenReflection/IReflection.php | 80 + .../TokenReflection/IReflectionClass.php | 624 +++ .../TokenReflection/IReflectionConstant.php | 128 + .../TokenReflection/IReflectionExtension.php | 108 + .../TokenReflection/IReflectionFunction.php | 72 + .../IReflectionFunctionBase.php | 135 + .../TokenReflection/IReflectionMethod.php | 182 + .../TokenReflection/IReflectionNamespace.php | 140 + .../TokenReflection/IReflectionParameter.php | 155 + .../TokenReflection/IReflectionProperty.php | 171 + .../Invalid/ReflectionClass.php | 1100 +++++ .../Invalid/ReflectionConstant.php | 403 ++ .../Invalid/ReflectionElement.php | 53 + .../Invalid/ReflectionFunction.php | 490 +++ .../TokenReflection/Php/IReflection.php | 36 + .../TokenReflection/Php/ReflectionClass.php | 935 +++++ .../Php/ReflectionConstant.php | 486 +++ .../Php/ReflectionExtension.php | 282 ++ .../Php/ReflectionFunction.php | 271 ++ .../TokenReflection/Php/ReflectionMethod.php | 385 ++ .../Php/ReflectionParameter.php | 368 ++ .../Php/ReflectionProperty.php | 348 ++ .../TokenReflection/ReflectionAnnotation.php | 484 +++ .../TokenReflection/ReflectionBase.php | 273 ++ .../TokenReflection/ReflectionClass.php | 1986 +++++++++ .../TokenReflection/ReflectionConstant.php | 392 ++ .../TokenReflection/ReflectionElement.php | 352 ++ .../TokenReflection/ReflectionFile.php | 144 + .../ReflectionFileNamespace.php | 412 ++ .../TokenReflection/ReflectionFunction.php | 204 + .../ReflectionFunctionBase.php | 440 ++ .../TokenReflection/ReflectionMethod.php | 775 ++++ .../TokenReflection/ReflectionNamespace.php | 558 +++ .../TokenReflection/ReflectionParameter.php | 639 +++ .../TokenReflection/ReflectionProperty.php | 572 +++ .../TokenReflection/Resolver.php | 288 ++ .../TokenReflection/Stream/FileStream.php | 50 + .../TokenReflection/Stream/StreamBase.php | 487 +++ .../TokenReflection/Stream/StringStream.php | 38 + apigen/templates/woodocs/404.latte | 23 + apigen/templates/woodocs/@elementlist.latte | 59 + apigen/templates/woodocs/@layout.latte | 186 + apigen/templates/woodocs/class.latte | 431 ++ apigen/templates/woodocs/combined.js.latte | 22 + apigen/templates/woodocs/config.neon | 56 + apigen/templates/woodocs/constant.latte | 67 + apigen/templates/woodocs/deprecated.latte | 137 + apigen/templates/woodocs/elementlist.js.latte | 15 + apigen/templates/woodocs/function.latte | 98 + .../woodocs/js/jquery.autocomplete.js | 799 ++++ apigen/templates/woodocs/js/jquery.cookie.js | 96 + apigen/templates/woodocs/js/jquery.min.js | 4 + .../woodocs/js/jquery.sortElements.js | 69 + apigen/templates/woodocs/js/jquery.sprintf.js | 8 + apigen/templates/woodocs/js/main.js | 292 ++ apigen/templates/woodocs/namespace.latte | 32 + apigen/templates/woodocs/opensearch.xml.latte | 21 + apigen/templates/woodocs/overview.latte | 57 + apigen/templates/woodocs/package.latte | 32 + .../woodocs/resources/bootstrap.min.css | 632 +++ .../templates/woodocs/resources/code-line.png | Bin 0 -> 371 bytes .../templates/woodocs/resources/collapsed.png | Bin 0 -> 288 bytes .../templates/woodocs/resources/combined.js | 1275 ++++++ .../templates/woodocs/resources/inherit.png | Bin 0 -> 152 bytes apigen/templates/woodocs/resources/resize.png | Bin 0 -> 216 bytes apigen/templates/woodocs/resources/sort.png | Bin 0 -> 171 bytes apigen/templates/woodocs/resources/style.css | 488 +++ .../woodocs/resources/tree-cleaner.png | Bin 0 -> 126 bytes .../woodocs/resources/tree-hasnext.png | Bin 0 -> 128 bytes .../templates/woodocs/resources/tree-last.png | Bin 0 -> 172 bytes .../woodocs/resources/tree-vertical.png | Bin 0 -> 127 bytes apigen/templates/woodocs/robots.txt.latte | 14 + apigen/templates/woodocs/sitemap.xml.latte | 36 + apigen/templates/woodocs/source.latte | 19 + apigen/templates/woodocs/todo.latte | 129 + apigen/templates/woodocs/tree.latte | 73 + 417 files changed, 101977 insertions(+), 1 deletion(-) create mode 100644 apigen/ApiGen/Backend.php create mode 100644 apigen/ApiGen/Config.php create mode 100644 apigen/ApiGen/ConfigException.php create mode 100644 apigen/ApiGen/Generator.php create mode 100644 apigen/ApiGen/IHelperSet.php create mode 100644 apigen/ApiGen/MacroSet.php create mode 100644 apigen/ApiGen/ReflectionBase.php create mode 100644 apigen/ApiGen/ReflectionClass.php create mode 100644 apigen/ApiGen/ReflectionConstant.php create mode 100644 apigen/ApiGen/ReflectionElement.php create mode 100644 apigen/ApiGen/ReflectionExtension.php create mode 100644 apigen/ApiGen/ReflectionFunction.php create mode 100644 apigen/ApiGen/ReflectionFunctionBase.php create mode 100644 apigen/ApiGen/ReflectionMethod.php create mode 100644 apigen/ApiGen/ReflectionMethodMagic.php create mode 100644 apigen/ApiGen/ReflectionParameter.php create mode 100644 apigen/ApiGen/ReflectionParameterMagic.php create mode 100644 apigen/ApiGen/ReflectionProperty.php create mode 100644 apigen/ApiGen/ReflectionPropertyMagic.php create mode 100644 apigen/ApiGen/SourceFilesFilterIterator.php create mode 100644 apigen/ApiGen/Template.php create mode 100644 apigen/ApiGen/Tree.php create mode 100644 apigen/CHANGELOG.md create mode 100644 apigen/LICENSE.md create mode 100644 apigen/README.md create mode 100755 apigen/apigen.bat create mode 100644 apigen/apigen.neon.example create mode 100644 apigen/apigen.php create mode 100644 apigen/libs/FSHL/FSHL/Generator.php create mode 100644 apigen/libs/FSHL/FSHL/Highlighter.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Cpp.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Css.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Html.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/HtmlOnly.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Java.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Javascript.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Minimal.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Neon.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Php.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Python.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Sql.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cache/Texy.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Cpp.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Css.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Html.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/HtmlOnly.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Java.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Javascript.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Minimal.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Neon.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Php.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Python.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Sql.php create mode 100644 apigen/libs/FSHL/FSHL/Lexer/Texy.php create mode 100644 apigen/libs/FSHL/FSHL/Output.php create mode 100644 apigen/libs/FSHL/FSHL/Output/Html.php create mode 100644 apigen/libs/FSHL/FSHL/Output/HtmlManual.php create mode 100644 apigen/libs/FSHL/LICENSE create mode 100644 apigen/libs/Nette/Nette/Application/Application.php create mode 100644 apigen/libs/Nette/Nette/Application/Diagnostics/RoutingPanel.php create mode 100644 apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml create mode 100644 apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Application/IPresenter.php create mode 100644 apigen/libs/Nette/Nette/Application/IPresenterFactory.php create mode 100644 apigen/libs/Nette/Nette/Application/IResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/IRouter.php create mode 100644 apigen/libs/Nette/Nette/Application/MicroPresenter.php create mode 100644 apigen/libs/Nette/Nette/Application/PresenterFactory.php create mode 100644 apigen/libs/Nette/Nette/Application/Request.php create mode 100644 apigen/libs/Nette/Nette/Application/Responses/FileResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/Responses/ForwardResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/Responses/JsonResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/Responses/RedirectResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/Responses/TextResponse.php create mode 100644 apigen/libs/Nette/Nette/Application/Routers/CliRouter.php create mode 100644 apigen/libs/Nette/Nette/Application/Routers/Route.php create mode 100644 apigen/libs/Nette/Nette/Application/Routers/RouteList.php create mode 100644 apigen/libs/Nette/Nette/Application/Routers/SimpleRouter.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/BadSignalException.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/Control.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/Form.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/IRenderable.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/ISignalReceiver.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/IStatePersistent.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/InvalidLinkException.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/Link.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/Multiplier.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/Presenter.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/PresenterComponent.php create mode 100644 apigen/libs/Nette/Nette/Application/UI/PresenterComponentReflection.php create mode 100644 apigen/libs/Nette/Nette/Application/exceptions.php create mode 100644 apigen/libs/Nette/Nette/Application/templates/error.phtml create mode 100644 apigen/libs/Nette/Nette/Caching/Cache.php create mode 100644 apigen/libs/Nette/Nette/Caching/IStorage.php create mode 100644 apigen/libs/Nette/Nette/Caching/OutputHelper.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/DevNullStorage.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/FileJournal.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/FileStorage.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/IJournal.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/MemcachedStorage.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/MemoryStorage.php create mode 100644 apigen/libs/Nette/Nette/Caching/Storages/PhpFileStorage.php create mode 100644 apigen/libs/Nette/Nette/ComponentModel/Component.php create mode 100644 apigen/libs/Nette/Nette/ComponentModel/Container.php create mode 100644 apigen/libs/Nette/Nette/ComponentModel/IComponent.php create mode 100644 apigen/libs/Nette/Nette/ComponentModel/IContainer.php create mode 100644 apigen/libs/Nette/Nette/ComponentModel/RecursiveComponentIterator.php create mode 100644 apigen/libs/Nette/Nette/Config/Adapters/IniAdapter.php create mode 100644 apigen/libs/Nette/Nette/Config/Adapters/NeonAdapter.php create mode 100644 apigen/libs/Nette/Nette/Config/Adapters/PhpAdapter.php create mode 100644 apigen/libs/Nette/Nette/Config/Compiler.php create mode 100644 apigen/libs/Nette/Nette/Config/CompilerExtension.php create mode 100644 apigen/libs/Nette/Nette/Config/Configurator.php create mode 100644 apigen/libs/Nette/Nette/Config/Extensions/ConstantsExtension.php create mode 100644 apigen/libs/Nette/Nette/Config/Extensions/NetteExtension.php create mode 100644 apigen/libs/Nette/Nette/Config/Extensions/PhpExtension.php create mode 100644 apigen/libs/Nette/Nette/Config/Helpers.php create mode 100644 apigen/libs/Nette/Nette/Config/IAdapter.php create mode 100644 apigen/libs/Nette/Nette/Config/Loader.php create mode 100644 apigen/libs/Nette/Nette/DI/Container.php create mode 100644 apigen/libs/Nette/Nette/DI/ContainerBuilder.php create mode 100644 apigen/libs/Nette/Nette/DI/Diagnostics/ContainerPanel.php create mode 100644 apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml create mode 100644 apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml create mode 100644 apigen/libs/Nette/Nette/DI/Helpers.php create mode 100644 apigen/libs/Nette/Nette/DI/IContainer.php create mode 100644 apigen/libs/Nette/Nette/DI/NestedAccessor.php create mode 100644 apigen/libs/Nette/Nette/DI/ServiceDefinition.php create mode 100644 apigen/libs/Nette/Nette/DI/Statement.php create mode 100644 apigen/libs/Nette/Nette/DI/exceptions.php create mode 100644 apigen/libs/Nette/Nette/Database/Connection.php create mode 100644 apigen/libs/Nette/Nette/Database/Diagnostics/ConnectionPanel.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/MsSqlDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/MySqlDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/OciDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/OdbcDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/PgSqlDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/Sqlite2Driver.php create mode 100644 apigen/libs/Nette/Nette/Database/Drivers/SqliteDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Helpers.php create mode 100644 apigen/libs/Nette/Nette/Database/IReflection.php create mode 100644 apigen/libs/Nette/Nette/Database/ISupplementalDriver.php create mode 100644 apigen/libs/Nette/Nette/Database/Reflection/ConventionalReflection.php create mode 100644 apigen/libs/Nette/Nette/Database/Reflection/DiscoveredReflection.php create mode 100644 apigen/libs/Nette/Nette/Database/Row.php create mode 100644 apigen/libs/Nette/Nette/Database/SqlLiteral.php create mode 100644 apigen/libs/Nette/Nette/Database/SqlPreprocessor.php create mode 100644 apigen/libs/Nette/Nette/Database/Statement.php create mode 100644 apigen/libs/Nette/Nette/Database/Table/ActiveRow.php create mode 100644 apigen/libs/Nette/Nette/Database/Table/GroupedSelection.php create mode 100644 apigen/libs/Nette/Nette/Database/Table/Selection.php create mode 100644 apigen/libs/Nette/Nette/Database/Table/SqlBuilder.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/Bar.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/BlueScreen.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/Debugger.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/DefaultBarPanel.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/FireLogger.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/Helpers.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/IBarPanel.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/Logger.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/shortcuts.php create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.dumps.panel.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.dumps.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.panel.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.memory.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bar.time.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/bluescreen.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/error.phtml create mode 100644 apigen/libs/Nette/Nette/Diagnostics/templates/netteQ.js create mode 100644 apigen/libs/Nette/Nette/Forms/Container.php create mode 100644 apigen/libs/Nette/Nette/Forms/ControlGroup.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/BaseControl.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/Button.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/Checkbox.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/HiddenField.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/ImageButton.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/MultiSelectBox.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/RadioList.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/SelectBox.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/SubmitButton.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/TextArea.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/TextBase.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/TextInput.php create mode 100644 apigen/libs/Nette/Nette/Forms/Controls/UploadControl.php create mode 100644 apigen/libs/Nette/Nette/Forms/Form.php create mode 100644 apigen/libs/Nette/Nette/Forms/IControl.php create mode 100644 apigen/libs/Nette/Nette/Forms/IFormRenderer.php create mode 100644 apigen/libs/Nette/Nette/Forms/ISubmitterControl.php create mode 100644 apigen/libs/Nette/Nette/Forms/Rendering/DefaultFormRenderer.php create mode 100644 apigen/libs/Nette/Nette/Forms/Rule.php create mode 100644 apigen/libs/Nette/Nette/Forms/Rules.php create mode 100644 apigen/libs/Nette/Nette/Http/Context.php create mode 100644 apigen/libs/Nette/Nette/Http/FileUpload.php create mode 100644 apigen/libs/Nette/Nette/Http/IRequest.php create mode 100644 apigen/libs/Nette/Nette/Http/IResponse.php create mode 100644 apigen/libs/Nette/Nette/Http/ISessionStorage.php create mode 100644 apigen/libs/Nette/Nette/Http/Request.php create mode 100644 apigen/libs/Nette/Nette/Http/RequestFactory.php create mode 100644 apigen/libs/Nette/Nette/Http/Response.php create mode 100644 apigen/libs/Nette/Nette/Http/Session.php create mode 100644 apigen/libs/Nette/Nette/Http/SessionSection.php create mode 100644 apigen/libs/Nette/Nette/Http/Url.php create mode 100644 apigen/libs/Nette/Nette/Http/UrlScript.php create mode 100644 apigen/libs/Nette/Nette/Http/UserStorage.php create mode 100644 apigen/libs/Nette/Nette/Iterators/CachingIterator.php create mode 100644 apigen/libs/Nette/Nette/Iterators/Filter.php create mode 100644 apigen/libs/Nette/Nette/Iterators/InstanceFilter.php create mode 100644 apigen/libs/Nette/Nette/Iterators/Mapper.php create mode 100644 apigen/libs/Nette/Nette/Iterators/RecursiveFilter.php create mode 100644 apigen/libs/Nette/Nette/Iterators/Recursor.php create mode 100644 apigen/libs/Nette/Nette/Latte/Compiler.php create mode 100644 apigen/libs/Nette/Nette/Latte/Engine.php create mode 100644 apigen/libs/Nette/Nette/Latte/HtmlNode.php create mode 100644 apigen/libs/Nette/Nette/Latte/IMacro.php create mode 100644 apigen/libs/Nette/Nette/Latte/MacroNode.php create mode 100644 apigen/libs/Nette/Nette/Latte/MacroTokenizer.php create mode 100644 apigen/libs/Nette/Nette/Latte/Macros/CacheMacro.php create mode 100644 apigen/libs/Nette/Nette/Latte/Macros/CoreMacros.php create mode 100644 apigen/libs/Nette/Nette/Latte/Macros/FormMacros.php create mode 100644 apigen/libs/Nette/Nette/Latte/Macros/MacroSet.php create mode 100644 apigen/libs/Nette/Nette/Latte/Macros/UIMacros.php create mode 100644 apigen/libs/Nette/Nette/Latte/Parser.php create mode 100644 apigen/libs/Nette/Nette/Latte/PhpWriter.php create mode 100644 apigen/libs/Nette/Nette/Latte/Token.php create mode 100644 apigen/libs/Nette/Nette/Latte/exceptions.php create mode 100644 apigen/libs/Nette/Nette/Loaders/AutoLoader.php create mode 100644 apigen/libs/Nette/Nette/Loaders/NetteLoader.php create mode 100644 apigen/libs/Nette/Nette/Loaders/RobotLoader.php create mode 100644 apigen/libs/Nette/Nette/Localization/ITranslator.php create mode 100644 apigen/libs/Nette/Nette/Mail/IMailer.php create mode 100644 apigen/libs/Nette/Nette/Mail/Message.php create mode 100644 apigen/libs/Nette/Nette/Mail/MimePart.php create mode 100644 apigen/libs/Nette/Nette/Mail/SendmailMailer.php create mode 100644 apigen/libs/Nette/Nette/Mail/SmtpMailer.php create mode 100644 apigen/libs/Nette/Nette/Reflection/Annotation.php create mode 100644 apigen/libs/Nette/Nette/Reflection/AnnotationsParser.php create mode 100644 apigen/libs/Nette/Nette/Reflection/ClassType.php create mode 100644 apigen/libs/Nette/Nette/Reflection/Extension.php create mode 100644 apigen/libs/Nette/Nette/Reflection/GlobalFunction.php create mode 100644 apigen/libs/Nette/Nette/Reflection/IAnnotation.php create mode 100644 apigen/libs/Nette/Nette/Reflection/Method.php create mode 100644 apigen/libs/Nette/Nette/Reflection/Parameter.php create mode 100644 apigen/libs/Nette/Nette/Reflection/Property.php create mode 100644 apigen/libs/Nette/Nette/Security/AuthenticationException.php create mode 100644 apigen/libs/Nette/Nette/Security/Diagnostics/UserPanel.php create mode 100644 apigen/libs/Nette/Nette/Security/Diagnostics/templates/UserPanel.panel.phtml create mode 100644 apigen/libs/Nette/Nette/Security/Diagnostics/templates/UserPanel.tab.phtml create mode 100644 apigen/libs/Nette/Nette/Security/IAuthenticator.php create mode 100644 apigen/libs/Nette/Nette/Security/IAuthorizator.php create mode 100644 apigen/libs/Nette/Nette/Security/IIdentity.php create mode 100644 apigen/libs/Nette/Nette/Security/IResource.php create mode 100644 apigen/libs/Nette/Nette/Security/IRole.php create mode 100644 apigen/libs/Nette/Nette/Security/IUserStorage.php create mode 100644 apigen/libs/Nette/Nette/Security/Identity.php create mode 100644 apigen/libs/Nette/Nette/Security/Permission.php create mode 100644 apigen/libs/Nette/Nette/Security/SimpleAuthenticator.php create mode 100644 apigen/libs/Nette/Nette/Security/User.php create mode 100644 apigen/libs/Nette/Nette/Templating/FileTemplate.php create mode 100644 apigen/libs/Nette/Nette/Templating/FilterException.php create mode 100644 apigen/libs/Nette/Nette/Templating/Helpers.php create mode 100644 apigen/libs/Nette/Nette/Templating/IFileTemplate.php create mode 100644 apigen/libs/Nette/Nette/Templating/ITemplate.php create mode 100644 apigen/libs/Nette/Nette/Templating/Template.php create mode 100644 apigen/libs/Nette/Nette/Utils/Arrays.php create mode 100644 apigen/libs/Nette/Nette/Utils/Finder.php create mode 100644 apigen/libs/Nette/Nette/Utils/Html.php create mode 100644 apigen/libs/Nette/Nette/Utils/Json.php create mode 100644 apigen/libs/Nette/Nette/Utils/LimitedScope.php create mode 100644 apigen/libs/Nette/Nette/Utils/MimeTypeDetector.php create mode 100644 apigen/libs/Nette/Nette/Utils/Neon.php create mode 100644 apigen/libs/Nette/Nette/Utils/Paginator.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/ClassType.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/Helpers.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/Method.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/Parameter.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/PhpLiteral.php create mode 100644 apigen/libs/Nette/Nette/Utils/PhpGenerator/Property.php create mode 100644 apigen/libs/Nette/Nette/Utils/SafeStream.php create mode 100644 apigen/libs/Nette/Nette/Utils/Strings.php create mode 100644 apigen/libs/Nette/Nette/Utils/Tokenizer.php create mode 100644 apigen/libs/Nette/Nette/Utils/Validators.php create mode 100644 apigen/libs/Nette/Nette/common/ArrayHash.php create mode 100644 apigen/libs/Nette/Nette/common/ArrayList.php create mode 100644 apigen/libs/Nette/Nette/common/Callback.php create mode 100644 apigen/libs/Nette/Nette/common/DateTime.php create mode 100644 apigen/libs/Nette/Nette/common/Environment.php create mode 100644 apigen/libs/Nette/Nette/common/Framework.php create mode 100644 apigen/libs/Nette/Nette/common/FreezableObject.php create mode 100644 apigen/libs/Nette/Nette/common/IFreezable.php create mode 100644 apigen/libs/Nette/Nette/common/Image.php create mode 100644 apigen/libs/Nette/Nette/common/Object.php create mode 100644 apigen/libs/Nette/Nette/common/ObjectMixin.php create mode 100644 apigen/libs/Nette/Nette/common/exceptions.php create mode 100644 apigen/libs/Nette/Nette/loader.php create mode 100644 apigen/libs/Nette/license.txt create mode 100644 apigen/libs/Texy/license.txt create mode 100644 apigen/libs/Texy/texy/libs/DTD.php create mode 100644 apigen/libs/Texy/texy/libs/RegExp.Patterns.php create mode 100644 apigen/libs/Texy/texy/libs/TexyConfigurator.php create mode 100644 apigen/libs/Texy/texy/libs/TexyHandlerInvocation.php create mode 100644 apigen/libs/Texy/texy/libs/TexyHtml.php create mode 100644 apigen/libs/Texy/texy/libs/TexyModifier.php create mode 100644 apigen/libs/Texy/texy/libs/TexyModule.php create mode 100644 apigen/libs/Texy/texy/libs/TexyObject.php create mode 100644 apigen/libs/Texy/texy/libs/TexyParser.php create mode 100644 apigen/libs/Texy/texy/libs/TexyUtf.php create mode 100644 apigen/libs/Texy/texy/modules/TexyBlockModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyBlockQuoteModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyEmoticonModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyFigureModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyHeadingModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyHorizLineModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyHtmlModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyHtmlOutputModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyImageModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyLinkModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyListModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyLongWordsModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyParagraphModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyPhraseModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyScriptModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyTableModule.php create mode 100644 apigen/libs/Texy/texy/modules/TexyTypographyModule.php create mode 100644 apigen/libs/Texy/texy/netterobots.txt create mode 100644 apigen/libs/Texy/texy/texy.php create mode 100644 apigen/libs/TokenReflection/LICENSE create mode 100644 apigen/libs/TokenReflection/TokenReflection/Broker.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Broker/Backend.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Broker/Backend/Memory.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Dummy/ReflectionClass.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/BaseException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/BrokerException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/FileProcessingException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/ParseException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/RuntimeException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Exception/StreamException.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflection.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionClass.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionConstant.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionExtension.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionFunction.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionFunctionBase.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionMethod.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionNamespace.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionParameter.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/IReflectionProperty.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionClass.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionConstant.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionElement.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionFunction.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/IReflection.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionClass.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionConstant.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionExtension.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionFunction.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionMethod.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionParameter.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Php/ReflectionProperty.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionAnnotation.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionBase.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionClass.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionConstant.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionElement.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionFile.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionFileNamespace.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionFunction.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionFunctionBase.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionMethod.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionNamespace.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionParameter.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/ReflectionProperty.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Resolver.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Stream/FileStream.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Stream/StreamBase.php create mode 100644 apigen/libs/TokenReflection/TokenReflection/Stream/StringStream.php create mode 100644 apigen/templates/woodocs/404.latte create mode 100644 apigen/templates/woodocs/@elementlist.latte create mode 100644 apigen/templates/woodocs/@layout.latte create mode 100644 apigen/templates/woodocs/class.latte create mode 100644 apigen/templates/woodocs/combined.js.latte create mode 100644 apigen/templates/woodocs/config.neon create mode 100644 apigen/templates/woodocs/constant.latte create mode 100644 apigen/templates/woodocs/deprecated.latte create mode 100644 apigen/templates/woodocs/elementlist.js.latte create mode 100644 apigen/templates/woodocs/function.latte create mode 100644 apigen/templates/woodocs/js/jquery.autocomplete.js create mode 100644 apigen/templates/woodocs/js/jquery.cookie.js create mode 100644 apigen/templates/woodocs/js/jquery.min.js create mode 100644 apigen/templates/woodocs/js/jquery.sortElements.js create mode 100644 apigen/templates/woodocs/js/jquery.sprintf.js create mode 100644 apigen/templates/woodocs/js/main.js create mode 100644 apigen/templates/woodocs/namespace.latte create mode 100644 apigen/templates/woodocs/opensearch.xml.latte create mode 100644 apigen/templates/woodocs/overview.latte create mode 100644 apigen/templates/woodocs/package.latte create mode 100644 apigen/templates/woodocs/resources/bootstrap.min.css create mode 100644 apigen/templates/woodocs/resources/code-line.png create mode 100644 apigen/templates/woodocs/resources/collapsed.png create mode 100644 apigen/templates/woodocs/resources/combined.js create mode 100644 apigen/templates/woodocs/resources/inherit.png create mode 100644 apigen/templates/woodocs/resources/resize.png create mode 100644 apigen/templates/woodocs/resources/sort.png create mode 100644 apigen/templates/woodocs/resources/style.css create mode 100644 apigen/templates/woodocs/resources/tree-cleaner.png create mode 100644 apigen/templates/woodocs/resources/tree-hasnext.png create mode 100644 apigen/templates/woodocs/resources/tree-last.png create mode 100644 apigen/templates/woodocs/resources/tree-vertical.png create mode 100644 apigen/templates/woodocs/robots.txt.latte create mode 100644 apigen/templates/woodocs/sitemap.xml.latte create mode 100644 apigen/templates/woodocs/source.latte create mode 100644 apigen/templates/woodocs/todo.latte create mode 100644 apigen/templates/woodocs/tree.latte diff --git a/.gitignore b/.gitignore index 496d7b1135e..c29bd24148c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Thumbs.db .settings* sftp-config.json /deploy/ +/wc-apidocs/ # Ignore all log files except for .htaccess /logs/* diff --git a/Gruntfile.js b/Gruntfile.js index a8e93f122d3..1e034355bd5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -126,6 +126,12 @@ module.exports = function( grunt ){ 'php index.php generate', 'sed -i "" "s/\\/\\/exit( \'Locked\' );/exit( \'Locked\' );/g" index.php', ].join( '&&' ) + }, + apigen: { + command: [ + 'cd apigen/', + 'php apigen.php --source ../ --destination ../wc-apidocs --download yes --template-config ./templates/woodocs/config.neon --title "WooCommerce" --exclude "*/mijireh/*" --exclude "*/includes/libraries/*" --exclude "*/i18n/*" --exclude "*/node_modules/*" --exclude "*/deploy/*"', + ].join( '&&' ) } }, @@ -139,7 +145,8 @@ module.exports = function( grunt ){ '!Gruntfile.js', '!sftp-config.json', '!package.json', - '!node_modules/**' + '!node_modules/**', + '!wc-apidocs/**' ], dest: 'deploy', expand: true, @@ -175,6 +182,10 @@ module.exports = function( grunt ){ 'shell:generatepot' ]); + grunt.registerTask( 'docs', [ + 'shell:apigen' + ]); + grunt.registerTask( 'dev', [ 'default', 'shell:txpull', diff --git a/apigen/ApiGen/Backend.php b/apigen/ApiGen/Backend.php new file mode 100644 index 00000000000..4f201387d1d --- /dev/null +++ b/apigen/ApiGen/Backend.php @@ -0,0 +1,301 @@ +generator = $generator; + $this->cacheTokenStreams = $cacheTokenStreams; + } + + /** + * Destructor. + * + * Deletes all cached token streams. + */ + public function __destruct() + { + foreach ($this->fileCache as $file) { + unlink($file); + } + } + + /** + * Adds a file to the backend storage. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token stream + * @param \TokenReflection\ReflectionFile $file File reflection object + * @return \TokenReflection\Broker\Backend\Memory + */ + public function addFile(TokenReflection\Stream\StreamBase $tokenStream, TokenReflection\ReflectionFile $file) + { + if ($this->cacheTokenStreams) { + $this->fileCache[$file->getName()] = $cacheFile = tempnam(sys_get_temp_dir(), 'trc'); + file_put_contents($cacheFile, serialize($tokenStream)); + } + + parent::addFile($tokenStream, $file); + + return $this; + } + + /** + * Returns an array of tokens for a particular file. + * + * @param string $fileName File name + * @return \TokenReflection\Stream + * @throws \RuntimeException If the token stream could not be returned. + */ + public function getFileTokens($fileName) + { + try { + if (!$this->isFileProcessed($fileName)) { + throw new InvalidArgumentException('File was not processed'); + } + + $realName = Broker::getRealPath($fileName); + if (!isset($this->fileCache[$realName])) { + throw new InvalidArgumentException('File is not in the cache'); + } + + $data = @file_get_contents($this->fileCache[$realName]); + if (false === $data) { + throw new RuntimeException('Cached file is not readable'); + } + $file = @unserialize($data); + if (false === $file) { + throw new RuntimeException('Stream could not be loaded from cache'); + } + + return $file; + } catch (\Exception $e) { + throw new RuntimeException(sprintf('Could not return token stream for file %s', $fileName), 0, $e); + } + } + + /** + * Prepares and returns used class lists. + * + * @return array + */ + protected function parseClassLists() + { + $allClasses = array( + self::TOKENIZED_CLASSES => array(), + self::INTERNAL_CLASSES => array(), + self::NONEXISTENT_CLASSES => array() + ); + + $declared = array_flip(array_merge(get_declared_classes(), get_declared_interfaces())); + + foreach ($this->getNamespaces() as $namespace) { + foreach ($namespace->getClasses() as $name => $trClass) { + $class = new ReflectionClass($trClass, $this->generator); + $allClasses[self::TOKENIZED_CLASSES][$name] = $class; + if (!$class->isDocumented()) { + continue; + } + + foreach (array_merge($trClass->getParentClasses(), $trClass->getInterfaces()) as $parentName => $parent) { + if ($parent->isInternal()) { + if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName])) { + $allClasses[self::INTERNAL_CLASSES][$parentName] = $parent; + } + } elseif (!$parent->isTokenized()) { + if (!isset($allClasses[self::NONEXISTENT_CLASSES][$parentName])) { + $allClasses[self::NONEXISTENT_CLASSES][$parentName] = $parent; + } + } + } + + $this->generator->checkMemory(); + } + } + + foreach ($allClasses[self::TOKENIZED_CLASSES] as $class) { + if (!$class->isDocumented()) { + continue; + } + + foreach ($class->getOwnMethods() as $method) { + $allClasses = $this->processFunction($declared, $allClasses, $method); + } + + foreach ($class->getOwnProperties() as $property) { + $annotations = $property->getAnnotations(); + + if (!isset($annotations['var'])) { + continue; + } + + foreach ($annotations['var'] as $doc) { + foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) { + if ($name = rtrim($name, '[]')) { + $name = Resolver::resolveClassFQN($name, $class->getNamespaceAliases(), $class->getNamespaceName()); + $allClasses = $this->addClass($declared, $allClasses, $name); + } + } + } + } + + $this->generator->checkMemory(); + } + + foreach ($this->getFunctions() as $function) { + $allClasses = $this->processFunction($declared, $allClasses, $function); + } + + array_walk_recursive($allClasses, function(&$reflection, $name, Generator $generator) { + if (!$reflection instanceof ReflectionClass) { + $reflection = new ReflectionClass($reflection, $generator); + } + }, $this->generator); + + return $allClasses; + } + + /** + * Processes a function/method and adds classes from annotations to the overall class array. + * + * @param array $declared Array of declared classes + * @param array $allClasses Array with all classes parsed so far + * @param \ApiGen\ReflectionFunction|\TokenReflection\IReflectionFunctionBase $function Function/method reflection + * @return array + */ + private function processFunction(array $declared, array $allClasses, $function) + { + static $parsedAnnotations = array('param', 'return', 'throws'); + + $annotations = $function->getAnnotations(); + foreach ($parsedAnnotations as $annotation) { + if (!isset($annotations[$annotation])) { + continue; + } + + foreach ($annotations[$annotation] as $doc) { + foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) { + if ($name) { + $name = Resolver::resolveClassFQN(rtrim($name, '[]'), $function->getNamespaceAliases(), $function->getNamespaceName()); + $allClasses = $this->addClass($declared, $allClasses, $name); + } + } + } + } + + foreach ($function->getParameters() as $param) { + if ($hint = $param->getClassName()) { + $allClasses = $this->addClass($declared, $allClasses, $hint); + } + } + + return $allClasses; + } + + /** + * Adds a class to list of classes. + * + * @param array $declared Array of declared classes + * @param array $allClasses Array with all classes parsed so far + * @param string $name Class name + * @return array + */ + private function addClass(array $declared, array $allClasses, $name) + { + $name = ltrim($name, '\\'); + + if (!isset($declared[$name]) || isset($allClasses[self::TOKENIZED_CLASSES][$name]) + || isset($allClasses[self::INTERNAL_CLASSES][$name]) || isset($allClasses[self::NONEXISTENT_CLASSES][$name]) + ) { + return $allClasses; + } + + $parameterClass = $this->getBroker()->getClass($name); + if ($parameterClass->isInternal()) { + $allClasses[self::INTERNAL_CLASSES][$name] = $parameterClass; + foreach (array_merge($parameterClass->getInterfaces(), $parameterClass->getParentClasses()) as $parentClass) { + if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName = $parentClass->getName()])) { + $allClasses[self::INTERNAL_CLASSES][$parentName] = $parentClass; + } + } + } elseif (!$parameterClass->isTokenized() && !isset($allClasses[self::NONEXISTENT_CLASSES][$name])) { + $allClasses[self::NONEXISTENT_CLASSES][$name] = $parameterClass; + } + + return $allClasses; + } + + /** + * Returns all constants from all namespaces. + * + * @return array + */ + public function getConstants() + { + $generator = $this->generator; + return array_map(function(IReflectionConstant $constant) use ($generator) { + return new ReflectionConstant($constant, $generator); + }, parent::getConstants()); + } + + /** + * Returns all functions from all namespaces. + * + * @return array + */ + public function getFunctions() + { + $generator = $this->generator; + return array_map(function(IReflectionFunction $function) use ($generator) { + return new ReflectionFunction($function, $generator); + }, parent::getFunctions()); + } +} diff --git a/apigen/ApiGen/Config.php b/apigen/ApiGen/Config.php new file mode 100644 index 00000000000..ea35741dc7c --- /dev/null +++ b/apigen/ApiGen/Config.php @@ -0,0 +1,602 @@ + '', + 'source' => array(), + 'destination' => '', + 'extensions' => array('php'), + 'exclude' => array(), + 'skipDocPath' => array(), + 'skipDocPrefix' => array(), + 'charset' => array('auto'), + 'main' => '', + 'title' => '', + 'baseUrl' => '', + 'googleCseId' => '', + 'googleCseLabel' => '', + 'googleAnalytics' => '', + 'templateConfig' => '', + 'allowedHtml' => array('b', 'i', 'a', 'ul', 'ol', 'li', 'p', 'br', 'var', 'samp', 'kbd', 'tt'), + 'groups' => 'auto', + 'autocomplete' => array('classes', 'constants', 'functions'), + 'accessLevels' => array('public', 'protected'), + 'internal' => false, + 'php' => true, + 'tree' => true, + 'deprecated' => false, + 'todo' => false, + 'download' => false, + 'sourceCode' => true, + 'report' => '', + 'undocumented' => '', + 'wipeout' => true, + 'quiet' => false, + 'progressbar' => true, + 'colors' => true, + 'updateCheck' => true, + 'debug' => false + ); + + /** + * File or directory path options. + * + * @var array + */ + private static $pathOptions = array( + 'config', + 'source', + 'destination', + 'templateConfig', + 'report' + ); + + /** + * Possible values for options. + * + * @var array + */ + private static $possibleOptionsValues = array( + 'groups' => array('auto', 'namespaces', 'packages', 'none'), + 'autocomplete' => array('classes', 'constants', 'functions', 'methods', 'properties', 'classconstants'), + 'accessLevels' => array('public', 'protected', 'private') + ); + + /** + * Initializes default configuration. + */ + public function __construct() + { + $templateDir = self::isInstalledByPear() ? '@data_dir@' . DIRECTORY_SEPARATOR . 'ApiGen' : realpath(__DIR__ . DIRECTORY_SEPARATOR . '..'); + self::$defaultConfig['templateConfig'] = $templateDir . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'config.neon'; + self::$defaultConfig['colors'] = 'WIN' !== substr(PHP_OS, 0, 3); + $this->config = self::$defaultConfig; + } + + /** + * Processes command line options. + * + * @param array $options + * @return \ApiGen\Config + */ + public function processCliOptions(array $options) + { + while ($option = current($options)) { + if (preg_match('~^--([a-z][-a-z]*[a-z])(?:=(.+))?$~', $option, $matches) || preg_match('~^-([a-z])=?(.*)~', $option, $matches)) { + $name = $matches[1]; + + if (!empty($matches[2])) { + $value = $matches[2]; + } else { + $next = next($options); + if (false === $next || '-' === $next{0}) { + prev($options); + $value = ''; + } else { + $value = $next; + } + } + + $this->options[$name][] = $value; + } + + next($options); + } + $this->options = array_map(function($value) { + return 1 === count($value) ? $value[0] : $value; + }, $this->options); + + // Compatibility with ApiGen 1.0 + foreach (array('config', 'source', 'destination') as $option) { + if (isset($this->options[$option{0}]) && !isset($this->options[$option])) { + $this->options[$option] = $this->options[$option{0}]; + } + unset($this->options[$option{0}]); + } + + return $this; + } + + /** + * Prepares configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in configuration is wrong. + */ + public function prepare() + { + // Command line options + $cli = array(); + $translator = array(); + foreach ($this->options as $option => $value) { + $converted = preg_replace_callback('~-([a-z])~', function($matches) { + return strtoupper($matches[1]); + }, $option); + + $cli[$converted] = $value; + $translator[$converted] = $option; + } + + $unknownOptions = array_keys(array_diff_key($cli, self::$defaultConfig)); + if (!empty($unknownOptions)) { + $originalOptions = array_map(function($option) { + return (1 === strlen($option) ? '-' : '--') . $option; + }, array_values(array_diff_key($translator, self::$defaultConfig))); + + $message = count($unknownOptions) > 1 + ? sprintf('Unknown command line options "%s"', implode('", "', $originalOptions)) + : sprintf('Unknown command line option "%s"', $originalOptions[0]); + throw new ConfigException($message); + } + + // Config file + $neon = array(); + if (empty($this->options) && $this->defaultConfigExists()) { + $this->options['config'] = $this->getDefaultConfigPath(); + } + if (isset($this->options['config']) && is_file($this->options['config'])) { + $neon = Neon::decode(file_get_contents($this->options['config'])); + foreach (self::$pathOptions as $option) { + if (!empty($neon[$option])) { + if (is_array($neon[$option])) { + foreach ($neon[$option] as $key => $value) { + $neon[$option][$key] = $this->getAbsolutePath($value); + } + } else { + $neon[$option] = $this->getAbsolutePath($neon[$option]); + } + } + } + + $unknownOptions = array_keys(array_diff_key($neon, self::$defaultConfig)); + if (!empty($unknownOptions)) { + $message = count($unknownOptions) > 1 + ? sprintf('Unknown config file options "%s"', implode('", "', $unknownOptions)) + : sprintf('Unknown config file option "%s"', $unknownOptions[0]); + throw new ConfigException($message); + } + } + + // Merge options + $this->config = array_merge(self::$defaultConfig, $neon, $cli); + + // Compatibility with old option name "undocumented" + if (!isset($this->config['report']) && isset($this->config['undocumented'])) { + $this->config['report'] = $this->config['undocumented']; + unset($this->config['undocumented']); + } + + foreach (self::$defaultConfig as $option => $valueDefinition) { + if (is_array($this->config[$option]) && !is_array($valueDefinition)) { + throw new ConfigException(sprintf('Option "%s" must be set only once', $option)); + } + + if (is_bool($this->config[$option]) && !is_bool($valueDefinition)) { + throw new ConfigException(sprintf('Option "%s" expects value', $option)); + } + + if (is_bool($valueDefinition) && !is_bool($this->config[$option])) { + // Boolean option + $value = strtolower($this->config[$option]); + if ('on' === $value || 'yes' === $value || 'true' === $value || '' === $value) { + $value = true; + } elseif ('off' === $value || 'no' === $value || 'false' === $value) { + $value = false; + } + $this->config[$option] = (bool) $value; + } elseif (is_array($valueDefinition)) { + // Array option + $this->config[$option] = array_unique((array) $this->config[$option]); + foreach ($this->config[$option] as $key => $value) { + $value = explode(',', $value); + while (count($value) > 1) { + array_push($this->config[$option], array_shift($value)); + } + $this->config[$option][$key] = array_shift($value); + } + $this->config[$option] = array_filter($this->config[$option]); + } + + // Check posssible values + if (!empty(self::$possibleOptionsValues[$option])) { + $values = self::$possibleOptionsValues[$option]; + + if (is_array($valueDefinition)) { + $this->config[$option] = array_filter($this->config[$option], function($value) use ($values) { + return in_array($value, $values); + }); + } elseif (!in_array($this->config[$option], $values)) { + $this->config[$option] = ''; + } + } + } + + // Unify character sets + $this->config['charset'] = array_map('strtoupper', $this->config['charset']); + + // Process options that specify a filesystem path + foreach (self::$pathOptions as $option) { + if (is_array($this->config[$option])) { + array_walk($this->config[$option], function(&$value) { + if (file_exists($value)) { + $value = realpath($value); + } + }); + usort($this->config[$option], 'strcasecmp'); + } else { + if (file_exists($this->config[$option])) { + $this->config[$option] = realpath($this->config[$option]); + } + } + } + + // Unify directory separators + foreach (array('exclude', 'skipDocPath') as $option) { + $this->config[$option] = array_map(function($mask) { + return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $mask); + }, $this->config[$option]); + usort($this->config[$option], 'strcasecmp'); + } + + // Unify prefixes + $this->config['skipDocPrefix'] = array_map(function($prefix) { + return ltrim($prefix, '\\'); + }, $this->config['skipDocPrefix']); + usort($this->config['skipDocPrefix'], 'strcasecmp'); + + // Base url without slash at the end + $this->config['baseUrl'] = rtrim($this->config['baseUrl'], '/'); + + // No progressbar in quiet mode + if ($this->config['quiet']) { + $this->config['progressbar'] = false; + } + + // Check + $this->check(); + + // Default template config + $this->config['template'] = array( + 'require' => array(), + 'resources' => array(), + 'templates' => array( + 'common' => array(), + 'optional' => array() + ) + ); + + // Merge template config + $this->config = array_merge_recursive($this->config, array('template' => Neon::decode(file_get_contents($fileName = $this->config['templateConfig'])))); + $this->config['template']['config'] = realpath($fileName); + + // Check template + $this->checkTemplate(); + + return $this; + } + + /** + * Checks configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in configuration is wrong. + */ + private function check() + { + if (!empty($this->config['config']) && !is_file($this->config['config'])) { + throw new ConfigException(sprintf('Config file "%s" doesn\'t exist', $this->config['config'])); + } + + if (empty($this->config['source'])) { + throw new ConfigException('Source is not set'); + } + foreach ($this->config['source'] as $source) { + if (!file_exists($source)) { + throw new ConfigException(sprintf('Source "%s" doesn\'t exist', $source)); + } + } + + if (empty($this->config['destination'])) { + throw new ConfigException('Destination is not set'); + } + + foreach ($this->config['extensions'] as $extension) { + if (!preg_match('~^[a-z\\d]+$~i', $extension)) { + throw new ConfigException(sprintf('Invalid file extension "%s"', $extension)); + } + } + + if (!is_file($this->config['templateConfig'])) { + throw new ConfigException(sprintf('Template config "%s" doesn\'t exist', $this->config['templateConfig'])); + } + + if (!empty($this->config['baseUrl']) && !preg_match('~^https?://(?:[-a-z0-9]+\.)+[a-z]{2,6}(?:/.*)?$~i', $this->config['baseUrl'])) { + throw new ConfigException(sprintf('Invalid base url "%s"', $this->config['baseUrl'])); + } + + if (!empty($this->config['googleCseId']) && !preg_match('~^\d{21}:[-a-z0-9_]{11}$~', $this->config['googleCseId'])) { + throw new ConfigException(sprintf('Invalid Google Custom Search ID "%s"', $this->config['googleCseId'])); + } + + if (!empty($this->config['googleAnalytics']) && !preg_match('~^UA\\-\\d+\\-\\d+$~', $this->config['googleAnalytics'])) { + throw new ConfigException(sprintf('Invalid Google Analytics tracking code "%s"', $this->config['googleAnalytics'])); + } + + if (empty($this->config['groups'])) { + throw new ConfigException('No supported groups value given'); + } + + if (empty($this->config['autocomplete'])) { + throw new ConfigException('No supported autocomplete value given'); + } + + if (empty($this->config['accessLevels'])) { + throw new ConfigException('No supported access level given'); + } + + return $this; + } + + /** + * Checks template configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in template configuration is wrong. + */ + private function checkTemplate() + { + $require = $this->config['template']['require']; + if (isset($require['min']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['min'])) { + throw new ConfigException(sprintf('Invalid minimal version definition "%s"', $require['min'])); + } + if (isset($require['max']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['max'])) { + throw new ConfigException(sprintf('Invalid maximal version definition "%s"', $require['max'])); + } + + $isMinOk = function($min) { + $min .= str_repeat('.0', 2 - substr_count($min, '.')); + return version_compare($min, Generator::VERSION, '<='); + }; + $isMaxOk = function($max) { + $max .= str_repeat('.0', 2 - substr_count($max, '.')); + return version_compare($max, Generator::VERSION, '>='); + }; + + if (isset($require['min'], $require['max']) && (!$isMinOk($require['min']) || !$isMaxOk($require['max']))) { + throw new ConfigException(sprintf('The template requires version from "%s" to "%s", you are using version "%s"', $require['min'], $require['max'], Generator::VERSION)); + } elseif (isset($require['min']) && !$isMinOk($require['min'])) { + throw new ConfigException(sprintf('The template requires version "%s" or newer, you are using version "%s"', $require['min'], Generator::VERSION)); + } elseif (isset($require['max']) && !$isMaxOk($require['max'])) { + throw new ConfigException(sprintf('The template requires version "%s" or older, you are using version "%s"', $require['max'], Generator::VERSION)); + } + + foreach (array('main', 'optional') as $section) { + foreach ($this->config['template']['templates'][$section] as $type => $config) { + if (!isset($config['filename'])) { + throw new ConfigException(sprintf('Filename for "%s" is not defined', $type)); + } + if (!isset($config['template'])) { + throw new ConfigException(sprintf('Template for "%s" is not defined', $type)); + } + if (!is_file(dirname($this->config['templateConfig']) . DIRECTORY_SEPARATOR . $config['template'])) { + throw new ConfigException(sprintf('Template for "%s" doesn\'t exist', $type)); + } + } + } + + return $this; + } + + /** + * Returns default configuration file path. + * + * @return string + */ + private function getDefaultConfigPath() + { + return getcwd() . DIRECTORY_SEPARATOR . 'apigen.neon'; + } + + /** + * Checks if default configuration file exists. + * + * @return boolean + */ + private function defaultConfigExists() + { + return is_file($this->getDefaultConfigPath()); + } + + /** + * Returns absolute path. + * + * @param string $path Path + * @return string + */ + private function getAbsolutePath($path) + { + if (preg_match('~/|[a-z]:~Ai', $path)) { + return $path; + } + + return dirname($this->options['config']) . DIRECTORY_SEPARATOR . $path; + } + + /** + * Checks if a configuration option exists. + * + * @param string $name Option name + * @return boolean + */ + public function __isset($name) + { + return isset($this->config[$name]); + } + + /** + * Returns a configuration option value. + * + * @param string $name Option name + * @return mixed + */ + public function __get($name) + { + return isset($this->config[$name]) ? $this->config[$name] : null; + } + + /** + * If the user requests help. + * + * @return boolean + */ + public function isHelpRequested() + { + if (empty($this->options) && !$this->defaultConfigExists()) { + return true; + } + + if (isset($this->options['h']) || isset($this->options['help'])) { + return true; + } + + return false; + } + + /** + * Returns help. + * + * @return string + */ + public function getHelp() + { + return <<<"HELP" +Usage: + apigen @option@--config@c <@value@path@c> [options] + apigen @option@--source@c <@value@dir@c|@value@file@c> @option@--destination@c <@value@dir@c> [options] + +Options: + @option@--config@c|@option@-c@c <@value@file@c> Config file + @option@--source@c|@option@-s@c <@value@dir@c|@value@file@c> Source file or directory to parse (can be used multiple times) + @option@--destination@c|@option@-d@c <@value@dir@c> Directory where to save the generated documentation + @option@--extensions@c <@value@list@c> List of allowed file extensions, default "@value@php@c" + @option@--exclude@c <@value@mask@c> Mask (case sensitive) to exclude file or directory from processing (can be used multiple times) + @option@--skip-doc-path@c <@value@mask@c> Don't generate documentation for elements from file or directory with this (case sensitive) mask (can be used multiple times) + @option@--skip-doc-prefix@c <@value@value@c> Don't generate documentation for elements with this (case sensitive) name prefix (can be used multiple times) + @option@--charset@c <@value@list@c> Character set of source files, default "@value@auto@c" + @option@--main@c <@value@value@c> Main project name prefix + @option@--title@c <@value@value@c> Title of generated documentation + @option@--base-url@c <@value@value@c> Documentation base URL + @option@--google-cse-id@c <@value@value@c> Google Custom Search ID + @option@--google-cse-label@c <@value@value@c> Google Custom Search label + @option@--google-analytics@c <@value@value@c> Google Analytics tracking code + @option@--template-config@c <@value@file@c> Template config file, default "@value@{$this->config['templateConfig']}@c" + @option@--allowed-html@c <@value@list@c> List of allowed HTML tags in documentation, default "@value@b,i,a,ul,ol,li,p,br,var,samp,kbd,tt@c" + @option@--groups@c <@value@value@c> How should elements be grouped in the menu. Default value is "@value@auto@c" (namespaces if available, packages otherwise) + @option@--autocomplete@c <@value@list@c> Element types for search input autocomplete. Default value is "@value@classes,constants,functions@c" + @option@--access-levels@c <@value@list@c> Generate documentation for methods and properties with given access level, default "@value@public,protected@c" + @option@--internal@c <@value@yes@c|@value@no@c> Generate documentation for elements marked as internal and display internal documentation parts, default "@value@no@c" + @option@--php@c <@value@yes@c|@value@no@c> Generate documentation for PHP internal classes, default "@value@yes@c" + @option@--tree@c <@value@yes@c|@value@no@c> Generate tree view of classes, interfaces, traits and exceptions, default "@value@yes@c" + @option@--deprecated@c <@value@yes@c|@value@no@c> Generate documentation for deprecated elements, default "@value@no@c" + @option@--todo@c <@value@yes@c|@value@no@c> Generate documentation of tasks, default "@value@no@c" + @option@--source-code@c <@value@yes@c|@value@no@c> Generate highlighted source code files, default "@value@yes@c" + @option@--download@c <@value@yes@c|@value@no@c> Add a link to download documentation as a ZIP archive, default "@value@no@c" + @option@--report@c <@value@file@c> Save a checkstyle report of poorly documented elements into a file + @option@--wipeout@c <@value@yes@c|@value@no@c> Wipe out the destination directory first, default "@value@yes@c" + @option@--quiet@c <@value@yes@c|@value@no@c> Don't display scaning and generating messages, default "@value@no@c" + @option@--progressbar@c <@value@yes@c|@value@no@c> Display progressbars, default "@value@yes@c" + @option@--colors@c <@value@yes@c|@value@no@c> Use colors, default "@value@no@c" on Windows, "@value@yes@c" on other systems + @option@--update-check@c <@value@yes@c|@value@no@c> Check for update, default "@value@yes@c" + @option@--debug@c <@value@yes@c|@value@no@c> Display additional information in case of an error, default "@value@no@c" + @option@--help@c|@option@-h@c Display this help + +Only source and destination directories are required - either set explicitly or using a config file. Configuration parameters passed via command line have precedence over parameters from a config file. + +Boolean options (those with possible values @value@yes@c|@value@no@c) do not have to have their values defined explicitly. Using @option@--debug@c and @option@--debug@c=@value@yes@c is exactly the same. + +Some options can have multiple values. You can do so either by using them multiple times or by separating values by a comma. That means that writing @option@--source@c=@value@file1.php@c @option@--source@c=@value@file2.php@c or @option@--source@c=@value@file1.php,file2.php@c is exactly the same. + +Files or directories specified by @option@--exclude@c will not be processed at all. +Elements from files within @option@--skip-doc-path@c or with @option@--skip-doc-prefix@c will be parsed but will not have their documentation generated. However if classes have any child classes, the full class tree will be generated and their inherited methods, properties and constants will be displayed (but will not be clickable). + +HELP; + } + + /** + * Checks if ApiGen is installed by PEAR. + * + * @return boolean + */ + public static function isInstalledByPear() + { + return false === strpos('@data_dir@', '@data_dir'); + } + + /** + * Checks if ApiGen is installed from downloaded archive. + * + * @return boolean + */ + public static function isInstalledByDownload() + { + return !self::isInstalledByPear(); + } +} diff --git a/apigen/ApiGen/ConfigException.php b/apigen/ApiGen/ConfigException.php new file mode 100644 index 00000000000..07709114b59 --- /dev/null +++ b/apigen/ApiGen/ConfigException.php @@ -0,0 +1,25 @@ + '[%s] %\' 6.2f%% %\' 3dMB', + 'width' => 80, + 'bar' => 64, + 'current' => 0, + 'maximum' => 1 + ); + + /** + * Sets configuration. + * + * @param array $config + */ + public function __construct(Config $config) + { + $this->config = $config; + $this->parsedClasses = new \ArrayObject(); + $this->parsedConstants = new \ArrayObject(); + $this->parsedFunctions = new \ArrayObject(); + } + + /** + * Scans and parses PHP files. + * + * @return array + * @throws \RuntimeException If no PHP files have been found. + */ + public function parse() + { + $files = array(); + + $flags = \RecursiveDirectoryIterator::CURRENT_AS_FILEINFO | \RecursiveDirectoryIterator::SKIP_DOTS; + if (defined('\\RecursiveDirectoryIterator::FOLLOW_SYMLINKS')) { + // Available from PHP 5.3.1 + $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + } + + foreach ($this->config->source as $source) { + $entries = array(); + if (is_dir($source)) { + foreach (new \RecursiveIteratorIterator(new SourceFilesFilterIterator(new \RecursiveDirectoryIterator($source, $flags), $this->config->exclude)) as $entry) { + if (!$entry->isFile()) { + continue; + } + $entries[] = $entry; + } + } elseif ($this->isPhar($source)) { + if (!extension_loaded('phar')) { + throw new RuntimeException('Phar extension is not loaded'); + } + foreach (new \RecursiveIteratorIterator(new \Phar($source, $flags)) as $entry) { + if (!$entry->isFile()) { + continue; + } + $entries[] = $entry; + } + } else { + $entries[] = new \SplFileInfo($source); + } + + $regexp = '~\\.' . implode('|', $this->config->extensions) . '$~i'; + foreach ($entries as $entry) { + if (!preg_match($regexp, $entry->getFilename())) { + continue; + } + + $pathName = $this->normalizePath($entry->getPathName()); + $files[$pathName] = $entry->getSize(); + if (false !== $entry->getRealPath() && $pathName !== $entry->getRealPath()) { + $this->symlinks[$entry->getRealPath()] = $pathName; + } + } + } + + if (empty($files)) { + throw new RuntimeException('No PHP files found'); + } + + if ($this->config->progressbar) { + $this->prepareProgressBar(array_sum($files)); + } + + $broker = new Broker(new Backend($this, !empty($this->config->report)), Broker::OPTION_DEFAULT & ~(Broker::OPTION_PARSE_FUNCTION_BODY | Broker::OPTION_SAVE_TOKEN_STREAM)); + + $errors = array(); + + foreach ($files as $fileName => $size) { + $content = file_get_contents($fileName); + $charset = $this->detectCharset($content); + $this->charsets[$fileName] = $charset; + $content = $this->toUtf($content, $charset); + + try { + $broker->processString($content, $fileName); + } catch (\Exception $e) { + $errors[] = $e; + } + + $this->incrementProgressBar($size); + $this->checkMemory(); + } + + // Classes + $this->parsedClasses->exchangeArray($broker->getClasses(Backend::TOKENIZED_CLASSES | Backend::INTERNAL_CLASSES | Backend::NONEXISTENT_CLASSES)); + $this->parsedClasses->uksort('strcasecmp'); + + // Constants + $this->parsedConstants->exchangeArray($broker->getConstants()); + $this->parsedConstants->uksort('strcasecmp'); + + // Functions + $this->parsedFunctions->exchangeArray($broker->getFunctions()); + $this->parsedFunctions->uksort('strcasecmp'); + + $documentedCounter = function($count, $element) { + return $count += (int) $element->isDocumented(); + }; + + return (object) array( + 'classes' => count($broker->getClasses(Backend::TOKENIZED_CLASSES)), + 'constants' => count($this->parsedConstants), + 'functions' => count($this->parsedFunctions), + 'internalClasses' => count($broker->getClasses(Backend::INTERNAL_CLASSES)), + 'documentedClasses' => array_reduce($broker->getClasses(Backend::TOKENIZED_CLASSES), $documentedCounter), + 'documentedConstants' => array_reduce($this->parsedConstants->getArrayCopy(), $documentedCounter), + 'documentedFunctions' => array_reduce($this->parsedFunctions->getArrayCopy(), $documentedCounter), + 'documentedInternalClasses' => array_reduce($broker->getClasses(Backend::INTERNAL_CLASSES), $documentedCounter), + 'errors' => $errors + ); + } + + /** + * Returns configuration. + * + * @return mixed + */ + public function getConfig() + { + return $this->config; + } + + /** + * Returns parsed class list. + * + * @return \ArrayObject + */ + public function getParsedClasses() + { + return $this->parsedClasses; + } + + /** + * Returns parsed constant list. + * + * @return \ArrayObject + */ + public function getParsedConstants() + { + return $this->parsedConstants; + } + + /** + * Returns parsed function list. + * + * @return \ArrayObject + */ + public function getParsedFunctions() + { + return $this->parsedFunctions; + } + + /** + * Wipes out the destination directory. + * + * @return boolean + */ + public function wipeOutDestination() + { + foreach ($this->getGeneratedFiles() as $path) { + if (is_file($path) && !@unlink($path)) { + return false; + } + } + + $archive = $this->getArchivePath(); + if (is_file($archive) && !@unlink($archive)) { + return false; + } + + return true; + } + + /** + * Generates API documentation. + * + * @throws \RuntimeException If destination directory is not writable. + */ + public function generate() + { + @mkdir($this->config->destination, 0755, true); + if (!is_dir($this->config->destination) || !is_writable($this->config->destination)) { + throw new RuntimeException(sprintf('Directory "%s" isn\'t writable', $this->config->destination)); + } + + // Copy resources + foreach ($this->config->template['resources'] as $resourceSource => $resourceDestination) { + // File + $resourcePath = $this->getTemplateDir() . DIRECTORY_SEPARATOR . $resourceSource; + if (is_file($resourcePath)) { + copy($resourcePath, $this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $resourceDestination)); + continue; + } + + // Dir + $iterator = Nette\Utils\Finder::findFiles('*')->from($resourcePath)->getIterator(); + foreach ($iterator as $item) { + copy($item->getPathName(), $this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $resourceDestination . DIRECTORY_SEPARATOR . $iterator->getSubPathName())); + } + } + + // Categorize by packages and namespaces + $this->categorize(); + + // Prepare progressbar + if ($this->config->progressbar) { + $max = count($this->packages) + + count($this->namespaces) + + count($this->classes) + + count($this->interfaces) + + count($this->traits) + + count($this->exceptions) + + count($this->constants) + + count($this->functions) + + count($this->config->template['templates']['common']) + + (int) !empty($this->config->report) + + (int) $this->config->tree + + (int) $this->config->deprecated + + (int) $this->config->todo + + (int) $this->config->download + + (int) $this->isSitemapEnabled() + + (int) $this->isOpensearchEnabled() + + (int) $this->isRobotsEnabled(); + + if ($this->config->sourceCode) { + $tokenizedFilter = function(ReflectionClass $class) { + return $class->isTokenized(); + }; + $max += count(array_filter($this->classes, $tokenizedFilter)) + + count(array_filter($this->interfaces, $tokenizedFilter)) + + count(array_filter($this->traits, $tokenizedFilter)) + + count(array_filter($this->exceptions, $tokenizedFilter)) + + count($this->constants) + + count($this->functions); + unset($tokenizedFilter); + } + + $this->prepareProgressBar($max); + } + + // Prepare template + $tmp = $this->config->destination . DIRECTORY_SEPARATOR . 'tmp'; + $this->deleteDir($tmp); + @mkdir($tmp, 0755, true); + $template = new Template($this); + $template->setCacheStorage(new Nette\Caching\Storages\PhpFileStorage($tmp)); + $template->generator = self::NAME; + $template->version = self::VERSION; + $template->config = $this->config; + + $this->registerCustomTemplateMacros($template); + + // Common files + $this->generateCommon($template); + + // Optional files + $this->generateOptional($template); + + // List of poorly documented elements + if (!empty($this->config->report)) { + $this->generateReport(); + } + + // List of deprecated elements + if ($this->config->deprecated) { + $this->generateDeprecated($template); + } + + // List of tasks + if ($this->config->todo) { + $this->generateTodo($template); + } + + // Classes/interfaces/traits/exceptions tree + if ($this->config->tree) { + $this->generateTree($template); + } + + // Generate packages summary + $this->generatePackages($template); + + // Generate namespaces summary + $this->generateNamespaces($template); + + // Generate classes, interfaces, traits, exceptions, constants and functions files + $this->generateElements($template); + + // Generate ZIP archive + if ($this->config->download) { + $this->generateArchive(); + } + + // Delete temporary directory + $this->deleteDir($tmp); + } + + /** + * Loads template-specific macro and helper libraries. + * + * @param \ApiGen\Template $template Template instance + */ + private function registerCustomTemplateMacros(Template $template) + { + $latte = new Nette\Latte\Engine(); + + if (!empty($this->config->template['options']['extensions'])) { + $this->output("Loading custom template macro and helper libraries\n"); + $broker = new Broker(new Broker\Backend\Memory(), 0); + + $baseDir = dirname($this->config->template['config']); + foreach ((array) $this->config->template['options']['extensions'] as $fileName) { + $pathName = $baseDir . DIRECTORY_SEPARATOR . $fileName; + if (is_file($pathName)) { + try { + $reflectionFile = $broker->processFile($pathName, true); + + foreach ($reflectionFile->getNamespaces() as $namespace) { + foreach ($namespace->getClasses() as $class) { + if ($class->isSubclassOf('ApiGen\\MacroSet')) { + // Macro set + + include $pathName; + call_user_func(array($class->getName(), 'install'), $latte->compiler); + + $this->output(sprintf(" %s (macro set)\n", $class->getName())); + } elseif ($class->implementsInterface('ApiGen\\IHelperSet')) { + // Helpers set + + include $pathName; + $className = $class->getName(); + $template->registerHelperLoader(callback(new $className($template), 'loader')); + + $this->output(sprintf(" %s (helper set)\n", $class->getName())); + } + } + } + } catch (\Exception $e) { + throw new \Exception(sprintf('Could not load macros and helpers from file "%s"', $pathName), 0, $e); + } + } else { + throw new \Exception(sprintf('Helper file "%s" does not exist.', $pathName)); + } + } + } + + $template->registerFilter($latte); + } + + /** + * Categorizes by packages and namespaces. + * + * @return \ApiGen\Generator + */ + private function categorize() + { + foreach (array('classes', 'constants', 'functions') as $type) { + foreach ($this->{'parsed' . ucfirst($type)} as $elementName => $element) { + if (!$element->isDocumented()) { + continue; + } + + $packageName = $element->getPseudoPackageName(); + $namespaceName = $element->getPseudoNamespaceName(); + + if ($element instanceof ReflectionConstant) { + $this->constants[$elementName] = $element; + $this->packages[$packageName]['constants'][$elementName] = $element; + $this->namespaces[$namespaceName]['constants'][$element->getShortName()] = $element; + } elseif ($element instanceof ReflectionFunction) { + $this->functions[$elementName] = $element; + $this->packages[$packageName]['functions'][$elementName] = $element; + $this->namespaces[$namespaceName]['functions'][$element->getShortName()] = $element; + } elseif ($element->isInterface()) { + $this->interfaces[$elementName] = $element; + $this->packages[$packageName]['interfaces'][$elementName] = $element; + $this->namespaces[$namespaceName]['interfaces'][$element->getShortName()] = $element; + } elseif ($element->isTrait()) { + $this->traits[$elementName] = $element; + $this->packages[$packageName]['traits'][$elementName] = $element; + $this->namespaces[$namespaceName]['traits'][$element->getShortName()] = $element; + } elseif ($element->isException()) { + $this->exceptions[$elementName] = $element; + $this->packages[$packageName]['exceptions'][$elementName] = $element; + $this->namespaces[$namespaceName]['exceptions'][$element->getShortName()] = $element; + } else { + $this->classes[$elementName] = $element; + $this->packages[$packageName]['classes'][$elementName] = $element; + $this->namespaces[$namespaceName]['classes'][$element->getShortName()] = $element; + } + } + } + + // Select only packages or namespaces + $userPackagesCount = count(array_diff(array_keys($this->packages), array('PHP', 'None'))); + $userNamespacesCount = count(array_diff(array_keys($this->namespaces), array('PHP', 'None'))); + + $namespacesEnabled = ('auto' === $this->config->groups && ($userNamespacesCount > 0 || 0 === $userPackagesCount)) || 'namespaces' === $this->config->groups; + $packagesEnabled = ('auto' === $this->config->groups && !$namespacesEnabled) || 'packages' === $this->config->groups; + + if ($namespacesEnabled) { + $this->packages = array(); + $this->namespaces = $this->sortGroups($this->namespaces); + } elseif ($packagesEnabled) { + $this->namespaces = array(); + $this->packages = $this->sortGroups($this->packages); + } else { + $this->namespaces = array(); + $this->packages = array(); + } + + return $this; + } + + /** + * Sorts and filters groups. + * + * @param array $groups + * @return array + */ + private function sortGroups(array $groups) + { + // Don't generate only 'None' groups + if (1 === count($groups) && isset($groups['None'])) { + return array(); + } + + $emptyList = array('classes' => array(), 'interfaces' => array(), 'traits' => array(), 'exceptions' => array(), 'constants' => array(), 'functions' => array()); + + $groupNames = array_keys($groups); + $lowerGroupNames = array_flip(array_map(function($y) { + return strtolower($y); + }, $groupNames)); + + foreach ($groupNames as $groupName) { + // Add missing parent groups + $parent = ''; + foreach (explode('\\', $groupName) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + if (!isset($lowerGroupNames[strtolower($parent)])) { + $groups[$parent] = $emptyList; + } + } + + // Add missing element types + foreach ($this->getElementTypes() as $type) { + if (!isset($groups[$groupName][$type])) { + $groups[$groupName][$type] = array(); + } + } + } + + $main = $this->config->main; + uksort($groups, function($one, $two) use ($main) { + // \ as separator has to be first + $one = str_replace('\\', ' ', $one); + $two = str_replace('\\', ' ', $two); + + if ($main) { + if (0 === strpos($one, $main) && 0 !== strpos($two, $main)) { + return -1; + } elseif (0 !== strpos($one, $main) && 0 === strpos($two, $main)) { + return 1; + } + } + + return strcasecmp($one, $two); + }); + + return $groups; + } + + /** + * Generates common files. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + */ + private function generateCommon(Template $template) + { + $template->namespace = null; + $template->namespaces = array_keys($this->namespaces); + $template->package = null; + $template->packages = array_keys($this->packages); + $template->class = null; + $template->classes = array_filter($this->classes, $this->getMainFilter()); + $template->interfaces = array_filter($this->interfaces, $this->getMainFilter()); + $template->traits = array_filter($this->traits, $this->getMainFilter()); + $template->exceptions = array_filter($this->exceptions, $this->getMainFilter()); + $template->constant = null; + $template->constants = array_filter($this->constants, $this->getMainFilter()); + $template->function = null; + $template->functions = array_filter($this->functions, $this->getMainFilter()); + $template->archive = basename($this->getArchivePath()); + + // Elements for autocomplete + $elements = array(); + $autocomplete = array_flip($this->config->autocomplete); + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $element) { + if ($element instanceof ReflectionClass) { + if (isset($autocomplete['classes'])) { + $elements[] = array('c', $element->getPrettyName()); + } + if (isset($autocomplete['methods'])) { + foreach ($element->getOwnMethods() as $method) { + $elements[] = array('m', $method->getPrettyName()); + } + foreach ($element->getOwnMagicMethods() as $method) { + $elements[] = array('mm', $method->getPrettyName()); + } + } + if (isset($autocomplete['properties'])) { + foreach ($element->getOwnProperties() as $property) { + $elements[] = array('p', $property->getPrettyName()); + } + foreach ($element->getOwnMagicProperties() as $property) { + $elements[] = array('mp', $property->getPrettyName()); + } + } + if (isset($autocomplete['classconstants'])) { + foreach ($element->getOwnConstants() as $constant) { + $elements[] = array('cc', $constant->getPrettyName()); + } + } + } elseif ($element instanceof ReflectionConstant && isset($autocomplete['constants'])) { + $elements[] = array('co', $element->getPrettyName()); + } elseif ($element instanceof ReflectionFunction && isset($autocomplete['functions'])) { + $elements[] = array('f', $element->getPrettyName()); + } + } + } + usort($elements, function($one, $two) { + return strcasecmp($one[1], $two[1]); + }); + $template->elements = $elements; + + foreach ($this->config->template['templates']['common'] as $source => $destination) { + $template + ->setFile($this->getTemplateDir() . DIRECTORY_SEPARATOR . $source) + ->save($this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $destination)); + + $this->incrementProgressBar(); + } + + unset($template->elements); + + $this->checkMemory(); + + return $this; + } + + /** + * Generates optional files. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + */ + private function generateOptional(Template $template) + { + if ($this->isSitemapEnabled()) { + $template + ->setFile($this->getTemplatePath('sitemap', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('sitemap', 'optional'))); + $this->incrementProgressBar(); + } + if ($this->isOpensearchEnabled()) { + $template + ->setFile($this->getTemplatePath('opensearch', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('opensearch', 'optional'))); + $this->incrementProgressBar(); + } + if ($this->isRobotsEnabled()) { + $template + ->setFile($this->getTemplatePath('robots', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('robots', 'optional'))); + $this->incrementProgressBar(); + } + + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of poorly documented elements. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If file isn't writable. + */ + private function generateReport() + { + // Function for element labels + $that = $this; + $labeler = function($element) use ($that) { + if ($element instanceof ReflectionClass) { + if ($element->isInterface()) { + $label = 'interface'; + } elseif ($element->isTrait()) { + $label = 'trait'; + } elseif ($element->isException()) { + $label = 'exception'; + } else { + $label = 'class'; + } + } elseif ($element instanceof ReflectionMethod) { + $label = 'method'; + } elseif ($element instanceof ReflectionFunction) { + $label = 'function'; + } elseif ($element instanceof ReflectionConstant) { + $label = 'constant'; + } elseif ($element instanceof ReflectionProperty) { + $label = 'property'; + } elseif ($element instanceof ReflectionParameter) { + $label = 'parameter'; + } + return sprintf('%s %s', $label, $element->getPrettyName()); + }; + + $list = array(); + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $parentElement) { + $fileName = $this->unPharPath($parentElement->getFileName()); + + if (!$parentElement->isValid()) { + $list[$fileName][] = array('error', 0, sprintf('Duplicate %s', $labeler($parentElement))); + continue; + } + + // Skip elements not from the main project + if (!$parentElement->isMain()) { + continue; + } + + // Internal elements don't have documentation + if ($parentElement->isInternal()) { + continue; + } + + $elements = array($parentElement); + if ($parentElement instanceof ReflectionClass) { + $elements = array_merge( + $elements, + array_values($parentElement->getOwnMethods()), + array_values($parentElement->getOwnConstants()), + array_values($parentElement->getOwnProperties()) + ); + } + + $tokens = $parentElement->getBroker()->getFileTokens($parentElement->getFileName()); + + foreach ($elements as $element) { + $line = $element->getStartLine(); + $label = $labeler($element); + + $annotations = $element->getAnnotations(); + + // Documentation + if (empty($element->shortDescription)) { + if (empty($annotations)) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of %s', $label)); + continue; + } + // Description + $list[$fileName][] = array('error', $line, sprintf('Missing description of %s', $label)); + } + + // Documentation of method + if ($element instanceof ReflectionMethod || $element instanceof ReflectionFunction) { + // Parameters + $unlimited = false; + foreach ($element->getParameters() as $no => $parameter) { + if (!isset($annotations['param'][$no])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of %s', $labeler($parameter))); + continue; + } + + if (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+\\$' . $parameter->getName() . ($parameter->isUnlimited() ? ',\\.{3}' : '') . ')?(?:\\s+.+)?$~s', $annotations['param'][$no])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of %s', $annotations['param'][$no], $labeler($parameter))); + } + + if ($unlimited && $parameter->isUnlimited()) { + $list[$fileName][] = array('warning', $line, sprintf('More than one unlimited parameters of %s', $labeler($element))); + } elseif ($parameter->isUnlimited()) { + $unlimited = true; + } + + unset($annotations['param'][$no]); + } + if (isset($annotations['param'])) { + foreach ($annotations['param'] as $annotation) { + $list[$fileName][] = array('warning', $line, sprintf('Existing documentation "%s" of nonexistent parameter of %s', $annotation, $label)); + } + } + + // Return values + $return = false; + $tokens->seek($element->getStartPosition()) + ->find(T_FUNCTION); + while ($tokens->next() && $tokens->key() < $element->getEndPosition()) { + $type = $tokens->getType(); + if (T_FUNCTION === $type) { + // Skip annonymous functions + $tokens->find('{')->findMatchingBracket(); + } elseif (T_RETURN === $type && !$tokens->skipWhitespaces()->is(';')) { + // Skip return without return value + $return = true; + break; + } + } + if ($return && !isset($annotations['return'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of return value of %s', $label)); + } elseif (isset($annotations['return'])) { + if (!$return && 'void' !== $annotations['return'][0] && ($element instanceof ReflectionFunction || (!$parentElement->isInterface() && !$element->isAbstract()))) { + $list[$fileName][] = array('warning', $line, sprintf('Existing documentation "%s" of nonexistent return value of %s', $annotations['return'][0], $label)); + } elseif (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+.+)?$~s', $annotations['return'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of return value of %s', $annotations['return'][0], $label)); + } + } + if (isset($annotations['return'][1])) { + $list[$fileName][] = array('warning', $line, sprintf('Duplicate documentation "%s" of return value of %s', $annotations['return'][1], $label)); + } + + // Throwing exceptions + $throw = false; + $tokens->seek($element->getStartPosition()) + ->find(T_FUNCTION); + while ($tokens->next() && $tokens->key() < $element->getEndPosition()) { + $type = $tokens->getType(); + if (T_TRY === $type) { + // Skip try + $tokens->find('{')->findMatchingBracket(); + } elseif (T_THROW === $type) { + $throw = true; + break; + } + } + if ($throw && !isset($annotations['throws'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of throwing an exception of %s', $label)); + } elseif (isset($annotations['throws']) && !preg_match('~^[\\w\\\\]+(?:\\|[\\w\\\\]+)*(?:\\s+.+)?$~s', $annotations['throws'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of throwing an exception of %s', $annotations['throws'][0], $label)); + } + } + + // Data type of constants & properties + if ($element instanceof ReflectionProperty || $element instanceof ReflectionConstant) { + if (!isset($annotations['var'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of the data type of %s', $label)); + } elseif (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+.+)?$~s', $annotations['var'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of the data type of %s', $annotations['var'][0], $label)); + } + + if (isset($annotations['var'][1])) { + $list[$fileName][] = array('warning', $line, sprintf('Duplicate documentation "%s" of the data type of %s', $annotations['var'][1], $label)); + } + } + } + unset($tokens); + } + } + uksort($list, 'strcasecmp'); + + $file = @fopen($this->config->report, 'w'); + if (false === $file) { + throw new RuntimeException(sprintf('File "%s" isn\'t writable', $this->config->report)); + } + fwrite($file, sprintf('%s', "\n")); + fwrite($file, sprintf('%s', "\n")); + foreach ($list as $fileName => $reports) { + fwrite($file, sprintf('%s%s', "\t", $fileName, "\n")); + + // Sort by line + usort($reports, function($one, $two) { + return strnatcmp($one[1], $two[1]); + }); + + foreach ($reports as $report) { + list($severity, $line, $message) = $report; + $message = preg_replace('~\\s+~u', ' ', $message); + fwrite($file, sprintf('%s%s', "\t\t", $severity, $line, htmlspecialchars($message), "\n")); + } + + fwrite($file, sprintf('%s%s', "\t", "\n")); + } + fwrite($file, sprintf('%s', "\n")); + fclose($file); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of deprecated elements. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateDeprecated(Template $template) + { + $this->prepareTemplate('deprecated'); + + $deprecatedFilter = function($element) { + return $element->isDeprecated(); + }; + + $template->deprecatedMethods = array(); + $template->deprecatedConstants = array(); + $template->deprecatedProperties = array(); + foreach (array_reverse($this->getElementTypes()) as $type) { + $template->{'deprecated' . ucfirst($type)} = array_filter(array_filter($this->$type, $this->getMainFilter()), $deprecatedFilter); + + if ('constants' === $type || 'functions' === $type) { + continue; + } + + foreach ($this->$type as $class) { + if (!$class->isMain()) { + continue; + } + + if ($class->isDeprecated()) { + continue; + } + + $template->deprecatedMethods = array_merge($template->deprecatedMethods, array_values(array_filter($class->getOwnMethods(), $deprecatedFilter))); + $template->deprecatedConstants = array_merge($template->deprecatedConstants, array_values(array_filter($class->getOwnConstants(), $deprecatedFilter))); + $template->deprecatedProperties = array_merge($template->deprecatedProperties, array_values(array_filter($class->getOwnProperties(), $deprecatedFilter))); + } + } + usort($template->deprecatedMethods, array($this, 'sortMethods')); + usort($template->deprecatedConstants, array($this, 'sortConstants')); + usort($template->deprecatedFunctions, array($this, 'sortFunctions')); + usort($template->deprecatedProperties, array($this, 'sortProperties')); + + $template + ->setFile($this->getTemplatePath('deprecated')) + ->save($this->forceDir($this->getTemplateFileName('deprecated'))); + + foreach ($this->getElementTypes() as $type) { + unset($template->{'deprecated' . ucfirst($type)}); + } + unset($template->deprecatedMethods); + unset($template->deprecatedProperties); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of tasks. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateTodo(Template $template) + { + $this->prepareTemplate('todo'); + + $todoFilter = function($element) { + return $element->hasAnnotation('todo'); + }; + + $template->todoMethods = array(); + $template->todoConstants = array(); + $template->todoProperties = array(); + foreach (array_reverse($this->getElementTypes()) as $type) { + $template->{'todo' . ucfirst($type)} = array_filter(array_filter($this->$type, $this->getMainFilter()), $todoFilter); + + if ('constants' === $type || 'functions' === $type) { + continue; + } + + foreach ($this->$type as $class) { + if (!$class->isMain()) { + continue; + } + + $template->todoMethods = array_merge($template->todoMethods, array_values(array_filter($class->getOwnMethods(), $todoFilter))); + $template->todoConstants = array_merge($template->todoConstants, array_values(array_filter($class->getOwnConstants(), $todoFilter))); + $template->todoProperties = array_merge($template->todoProperties, array_values(array_filter($class->getOwnProperties(), $todoFilter))); + } + } + usort($template->todoMethods, array($this, 'sortMethods')); + usort($template->todoConstants, array($this, 'sortConstants')); + usort($template->todoFunctions, array($this, 'sortFunctions')); + usort($template->todoProperties, array($this, 'sortProperties')); + + $template + ->setFile($this->getTemplatePath('todo')) + ->save($this->forceDir($this->getTemplateFileName('todo'))); + + foreach ($this->getElementTypes() as $type) { + unset($template->{'todo' . ucfirst($type)}); + } + unset($template->todoMethods); + unset($template->todoProperties); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates classes/interfaces/traits/exceptions tree. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateTree(Template $template) + { + $this->prepareTemplate('tree'); + + $classTree = array(); + $interfaceTree = array(); + $traitTree = array(); + $exceptionTree = array(); + + $processed = array(); + foreach ($this->parsedClasses as $className => $reflection) { + if (!$reflection->isMain() || !$reflection->isDocumented() || isset($processed[$className])) { + continue; + } + + if (null === $reflection->getParentClassName()) { + // No parent classes + if ($reflection->isInterface()) { + $t = &$interfaceTree; + } elseif ($reflection->isTrait()) { + $t = &$traitTree; + } elseif ($reflection->isException()) { + $t = &$exceptionTree; + } else { + $t = &$classTree; + } + } else { + foreach (array_values(array_reverse($reflection->getParentClasses())) as $level => $parent) { + if (0 === $level) { + // The topmost parent decides about the reflection type + if ($parent->isInterface()) { + $t = &$interfaceTree; + } elseif ($parent->isTrait()) { + $t = &$traitTree; + } elseif ($parent->isException()) { + $t = &$exceptionTree; + } else { + $t = &$classTree; + } + } + $parentName = $parent->getName(); + + if (!isset($t[$parentName])) { + $t[$parentName] = array(); + $processed[$parentName] = true; + ksort($t, SORT_STRING); + } + + $t = &$t[$parentName]; + } + } + $t[$className] = array(); + ksort($t, SORT_STRING); + $processed[$className] = true; + unset($t); + } + + $template->classTree = new Tree($classTree, $this->parsedClasses); + $template->interfaceTree = new Tree($interfaceTree, $this->parsedClasses); + $template->traitTree = new Tree($traitTree, $this->parsedClasses); + $template->exceptionTree = new Tree($exceptionTree, $this->parsedClasses); + + $template + ->setFile($this->getTemplatePath('tree')) + ->save($this->forceDir($this->getTemplateFileName('tree'))); + + unset($template->classTree); + unset($template->interfaceTree); + unset($template->traitTree); + unset($template->exceptionTree); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates packages summary. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generatePackages(Template $template) + { + if (empty($this->packages)) { + return $this; + } + + $this->prepareTemplate('package'); + + $template->namespace = null; + + foreach ($this->packages as $packageName => $package) { + $template->package = $packageName; + $template->subpackages = array_filter($template->packages, function($subpackageName) use ($packageName) { + return (bool) preg_match('~^' . preg_quote($packageName) . '\\\\[^\\\\]+$~', $subpackageName); + }); + $template->classes = $package['classes']; + $template->interfaces = $package['interfaces']; + $template->traits = $package['traits']; + $template->exceptions = $package['exceptions']; + $template->constants = $package['constants']; + $template->functions = $package['functions']; + $template + ->setFile($this->getTemplatePath('package')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getPackageUrl($packageName)); + + $this->incrementProgressBar(); + } + unset($template->subpackages); + + $this->checkMemory(); + + return $this; + } + + /** + * Generates namespaces summary. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateNamespaces(Template $template) + { + if (empty($this->namespaces)) { + return $this; + } + + $this->prepareTemplate('namespace'); + + $template->package = null; + + foreach ($this->namespaces as $namespaceName => $namespace) { + $template->namespace = $namespaceName; + $template->subnamespaces = array_filter($template->namespaces, function($subnamespaceName) use ($namespaceName) { + return (bool) preg_match('~^' . preg_quote($namespaceName) . '\\\\[^\\\\]+$~', $subnamespaceName); + }); + $template->classes = $namespace['classes']; + $template->interfaces = $namespace['interfaces']; + $template->traits = $namespace['traits']; + $template->exceptions = $namespace['exceptions']; + $template->constants = $namespace['constants']; + $template->functions = $namespace['functions']; + $template + ->setFile($this->getTemplatePath('namespace')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getNamespaceUrl($namespaceName)); + + $this->incrementProgressBar(); + } + unset($template->subnamespaces); + + $this->checkMemory(); + + return $this; + } + + /** + * Generate classes, interfaces, traits, exceptions, constants and functions files. + * + * @param Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateElements(Template $template) + { + if (!empty($this->classes) || !empty($this->interfaces) || !empty($this->traits) || !empty($this->exceptions)) { + $this->prepareTemplate('class'); + } + if (!empty($this->constants)) { + $this->prepareTemplate('constant'); + } + if (!empty($this->functions)) { + $this->prepareTemplate('function'); + } + if ($this->config->sourceCode) { + $this->prepareTemplate('source'); + + $fshl = new FSHL\Highlighter(new FSHL\Output\Html(), FSHL\Highlighter::OPTION_TAB_INDENT | FSHL\Highlighter::OPTION_LINE_COUNTER); + $fshl->setLexer(new FSHL\Lexer\Php()); + } + + // Add @usedby annotation + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $parentElement) { + $elements = array($parentElement); + if ($parentElement instanceof ReflectionClass) { + $elements = array_merge( + $elements, + array_values($parentElement->getOwnMethods()), + array_values($parentElement->getOwnConstants()), + array_values($parentElement->getOwnProperties()) + ); + } + foreach ($elements as $element) { + $uses = $element->getAnnotation('uses'); + if (null === $uses) { + continue; + } + foreach ($uses as $value) { + list($link, $description) = preg_split('~\s+|$~', $value, 2); + $resolved = $this->resolveElement($link, $element); + if (null !== $resolved) { + $resolved->addAnnotation('usedby', $element->getPrettyName() . ' ' . $description); + } + } + } + } + } + + $template->package = null; + $template->namespace = null; + $template->classes = $this->classes; + $template->interfaces = $this->interfaces; + $template->traits = $this->traits; + $template->exceptions = $this->exceptions; + $template->constants = $this->constants; + $template->functions = $this->functions; + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $element) { + if (!empty($this->namespaces)) { + $template->namespace = $namespaceName = $element->getPseudoNamespaceName(); + $template->classes = $this->namespaces[$namespaceName]['classes']; + $template->interfaces = $this->namespaces[$namespaceName]['interfaces']; + $template->traits = $this->namespaces[$namespaceName]['traits']; + $template->exceptions = $this->namespaces[$namespaceName]['exceptions']; + $template->constants = $this->namespaces[$namespaceName]['constants']; + $template->functions = $this->namespaces[$namespaceName]['functions']; + } elseif (!empty($this->packages)) { + $template->package = $packageName = $element->getPseudoPackageName(); + $template->classes = $this->packages[$packageName]['classes']; + $template->interfaces = $this->packages[$packageName]['interfaces']; + $template->traits = $this->packages[$packageName]['traits']; + $template->exceptions = $this->packages[$packageName]['exceptions']; + $template->constants = $this->packages[$packageName]['constants']; + $template->functions = $this->packages[$packageName]['functions']; + } + + $template->class = null; + $template->constant = null; + $template->function = null; + if ($element instanceof ReflectionClass) { + // Class + $template->tree = array_merge(array_reverse($element->getParentClasses()), array($element)); + + $template->directSubClasses = $element->getDirectSubClasses(); + uksort($template->directSubClasses, 'strcasecmp'); + $template->indirectSubClasses = $element->getIndirectSubClasses(); + uksort($template->indirectSubClasses, 'strcasecmp'); + + $template->directImplementers = $element->getDirectImplementers(); + uksort($template->directImplementers, 'strcasecmp'); + $template->indirectImplementers = $element->getIndirectImplementers(); + uksort($template->indirectImplementers, 'strcasecmp'); + + $template->directUsers = $element->getDirectUsers(); + uksort($template->directUsers, 'strcasecmp'); + $template->indirectUsers = $element->getIndirectUsers(); + uksort($template->indirectUsers, 'strcasecmp'); + + $template->class = $element; + + $template + ->setFile($this->getTemplatePath('class')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getClassUrl($element)); + } elseif ($element instanceof ReflectionConstant) { + // Constant + $template->constant = $element; + + $template + ->setFile($this->getTemplatePath('constant')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getConstantUrl($element)); + } elseif ($element instanceof ReflectionFunction) { + // Function + $template->function = $element; + + $template + ->setFile($this->getTemplatePath('function')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getFunctionUrl($element)); + } + + $this->incrementProgressBar(); + + // Generate source codes + if ($this->config->sourceCode && $element->isTokenized()) { + $template->fileName = $this->getRelativePath($element->getFileName()); + $template->source = $fshl->highlight($this->toUtf(file_get_contents($element->getFileName()), $this->charsets[$element->getFileName()])); + $template + ->setFile($this->getTemplatePath('source')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getSourceUrl($element, false)); + + $this->incrementProgressBar(); + } + + $this->checkMemory(); + } + } + + return $this; + } + + /** + * Creates ZIP archive. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If something went wrong. + */ + private function generateArchive() + { + if (!extension_loaded('zip')) { + throw new RuntimeException('Extension zip is not loaded'); + } + + $archive = new \ZipArchive(); + if (true !== $archive->open($this->getArchivePath(), \ZipArchive::CREATE)) { + throw new RuntimeException('Could not open ZIP archive'); + } + + $archive->setArchiveComment(trim(sprintf('%s API documentation generated by %s %s on %s', $this->config->title, self::NAME, self::VERSION, date('Y-m-d H:i:s')))); + + $directory = Nette\Utils\Strings::webalize(trim(sprintf('%s API documentation', $this->config->title)), null, false); + $destinationLength = strlen($this->config->destination); + foreach ($this->getGeneratedFiles() as $file) { + if (is_file($file)) { + $archive->addFile($file, $directory . DIRECTORY_SEPARATOR . substr($file, $destinationLength + 1)); + } + } + + if (false === $archive->close()) { + throw new RuntimeException('Could not save ZIP archive'); + } + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Tries to resolve string as class, interface or exception name. + * + * @param string $className Class name description + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionClass + */ + public function getClass($className, $namespace = '') + { + if (isset($this->parsedClasses[$namespace . '\\' . $className])) { + $class = $this->parsedClasses[$namespace . '\\' . $className]; + } elseif (isset($this->parsedClasses[ltrim($className, '\\')])) { + $class = $this->parsedClasses[ltrim($className, '\\')]; + } else { + return null; + } + + // Class is not "documented" + if (!$class->isDocumented()) { + return null; + } + + return $class; + } + + /** + * Tries to resolve type as constant name. + * + * @param string $constantName Constant name + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionConstant + */ + public function getConstant($constantName, $namespace = '') + { + if (isset($this->parsedConstants[$namespace . '\\' . $constantName])) { + $constant = $this->parsedConstants[$namespace . '\\' . $constantName]; + } elseif (isset($this->parsedConstants[ltrim($constantName, '\\')])) { + $constant = $this->parsedConstants[ltrim($constantName, '\\')]; + } else { + return null; + } + + // Constant is not "documented" + if (!$constant->isDocumented()) { + return null; + } + + return $constant; + } + + /** + * Tries to resolve type as function name. + * + * @param string $functionName Function name + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionFunction + */ + public function getFunction($functionName, $namespace = '') + { + if (isset($this->parsedFunctions[$namespace . '\\' . $functionName])) { + $function = $this->parsedFunctions[$namespace . '\\' . $functionName]; + } elseif (isset($this->parsedFunctions[ltrim($functionName, '\\')])) { + $function = $this->parsedFunctions[ltrim($functionName, '\\')]; + } else { + return null; + } + + // Function is not "documented" + if (!$function->isDocumented()) { + return null; + } + + return $function; + } + + /** + * Tries to parse a definition of a class/method/property/constant/function and returns the appropriate instance if successful. + * + * @param string $definition Definition + * @param \ApiGen\ReflectionElement|\ApiGen\ReflectionParameter $context Link context + * @param string $expectedName Expected element name + * @return \ApiGen\ReflectionElement|null + */ + public function resolveElement($definition, $context, &$expectedName = null) + { + // No simple type resolving + static $types = array( + 'boolean' => 1, 'integer' => 1, 'float' => 1, 'string' => 1, + 'array' => 1, 'object' => 1, 'resource' => 1, 'callback' => 1, + 'callable' => 1, 'null' => 1, 'false' => 1, 'true' => 1, 'mixed' => 1 + ); + + if (empty($definition) || isset($types[$definition])) { + return null; + } + + $originalContext = $context; + + if ($context instanceof ReflectionParameter && null === $context->getDeclaringClassName()) { + // Parameter of function in namespace or global space + $context = $this->getFunction($context->getDeclaringFunctionName()); + } elseif ($context instanceof ReflectionMethod || $context instanceof ReflectionParameter + || ($context instanceof ReflectionConstant && null !== $context->getDeclaringClassName()) + || $context instanceof ReflectionProperty + ) { + // Member of a class + $context = $this->getClass($context->getDeclaringClassName()); + } + + if (null === $context) { + return null; + } + + // self, $this references + if ('self' === $definition || '$this' === $definition) { + return $context instanceof ReflectionClass ? $context : null; + } + + $definitionBase = substr($definition, 0, strcspn($definition, '\\:')); + $namespaceAliases = $context->getNamespaceAliases(); + if (!empty($definitionBase) && isset($namespaceAliases[$definitionBase]) && $definition !== ($className = \TokenReflection\Resolver::resolveClassFQN($definition, $namespaceAliases, $context->getNamespaceName()))) { + // Aliased class + $expectedName = $className; + + if (false === strpos($className, ':')) { + return $this->getClass($className, $context->getNamespaceName()); + } else { + $definition = $className; + } + } elseif ($class = $this->getClass($definition, $context->getNamespaceName())) { + // Class + return $class; + } elseif ($constant = $this->getConstant($definition, $context->getNamespaceName())) { + // Constant + return $constant; + } elseif (($function = $this->getFunction($definition, $context->getNamespaceName())) + || ('()' === substr($definition, -2) && ($function = $this->getFunction(substr($definition, 0, -2), $context->getNamespaceName()))) + ) { + // Function + return $function; + } + + if (($pos = strpos($definition, '::')) || ($pos = strpos($definition, '->'))) { + // Class::something or Class->something + if (0 === strpos($definition, 'parent::') && ($parentClassName = $context->getParentClassName())) { + $context = $this->getClass($parentClassName); + } elseif (0 !== strpos($definition, 'self::')) { + $class = $this->getClass(substr($definition, 0, $pos), $context->getNamespaceName()); + + if (null === $class) { + $class = $this->getClass(\TokenReflection\Resolver::resolveClassFQN(substr($definition, 0, $pos), $context->getNamespaceAliases(), $context->getNamespaceName())); + } + + $context = $class; + } + + $definition = substr($definition, $pos + 2); + } elseif ($originalContext instanceof ReflectionParameter) { + return null; + } + + // No usable context + if (null === $context || $context instanceof ReflectionConstant || $context instanceof ReflectionFunction) { + return null; + } + + if ($context->hasProperty($definition)) { + // Class property + return $context->getProperty($definition); + } elseif ('$' === $definition{0} && $context->hasProperty(substr($definition, 1))) { + // Class $property + return $context->getProperty(substr($definition, 1)); + } elseif ($context->hasMethod($definition)) { + // Class method + return $context->getMethod($definition); + } elseif ('()' === substr($definition, -2) && $context->hasMethod(substr($definition, 0, -2))) { + // Class method() + return $context->getMethod(substr($definition, 0, -2)); + } elseif ($context->hasConstant($definition)) { + // Class constant + return $context->getConstant($definition); + } + + return null; + } + + /** + * Prints message if printing is enabled. + * + * @param string $message Output message + */ + public function output($message) + { + if (!$this->config->quiet) { + echo $this->colorize($message); + } + } + + /** + * Colorizes message or removes placeholders if OS doesn't support colors. + * + * @param string $message + * @return string + */ + public function colorize($message) + { + static $placeholders = array( + '@header@' => "\x1b[1;34m", + '@count@' => "\x1b[1;34m", + '@option@' => "\x1b[0;36m", + '@value@' => "\x1b[0;32m", + '@error@' => "\x1b[0;31m", + '@c' => "\x1b[0m" + ); + + if (!$this->config->colors) { + $placeholders = array_fill_keys(array_keys($placeholders), ''); + } + + return strtr($message, $placeholders); + } + + /** + * Returns header. + * + * @return string + */ + public function getHeader() + { + $name = sprintf('%s %s', self::NAME, self::VERSION); + return sprintf("@header@%s@c\n%s\n", $name, str_repeat('-', strlen($name))); + } + + /** + * Removes phar:// from the path. + * + * @param string $path Path + * @return string + */ + public function unPharPath($path) + { + if (0 === strpos($path, 'phar://')) { + $path = substr($path, 7); + } + return $path; + } + + /** + * Adds phar:// to the path. + * + * @param string $path Path + * @return string + */ + private function pharPath($path) + { + return 'phar://' . $path; + } + + /** + * Checks if given path is a phar. + * + * @param string $path + * @return boolean + */ + private function isPhar($path) + { + return (bool) preg_match('~\\.phar(?:\\.zip|\\.tar|(?:(?:\\.tar)?(?:\\.gz|\\.bz2))|$)~i', $path); + } + + /** + * Normalizes directory separators in given path. + * + * @param string $path Path + * @return string + */ + private function normalizePath($path) + { + $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); + $path = str_replace('phar:\\\\', 'phar://', $path); + return $path; + } + + /** + * Prepares the progressbar. + * + * @param integer $maximum Maximum progressbar value + */ + private function prepareProgressBar($maximum = 1) + { + if (!$this->config->progressbar) { + return; + } + + $this->progressbar['current'] = 0; + $this->progressbar['maximum'] = $maximum; + } + + /** + * Increments the progressbar by one. + * + * @param integer $increment Progressbar increment + */ + private function incrementProgressBar($increment = 1) + { + if (!$this->config->progressbar) { + return; + } + + echo str_repeat(chr(0x08), $this->progressbar['width']); + + $this->progressbar['current'] += $increment; + + $percent = $this->progressbar['current'] / $this->progressbar['maximum']; + + $progress = str_pad(str_pad('>', round($percent * $this->progressbar['bar']), '=', STR_PAD_LEFT), $this->progressbar['bar'], ' ', STR_PAD_RIGHT); + + echo sprintf($this->progressbar['skeleton'], $progress, $percent * 100, round(memory_get_usage(true) / 1024 / 1024)); + + if ($this->progressbar['current'] === $this->progressbar['maximum']) { + echo "\n"; + } + } + + /** + * Checks memory usage. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If there is unsufficient reserve of memory. + */ + public function checkMemory() + { + static $limit = null; + if (null === $limit) { + $value = ini_get('memory_limit'); + $unit = substr($value, -1); + if ('-1' === $value) { + $limit = 0; + } elseif ('G' === $unit) { + $limit = (int) $value * 1024 * 1024 * 1024; + } elseif ('M' === $unit) { + $limit = (int) $value * 1024 * 1024; + } else { + $limit = (int) $value; + } + } + + if ($limit && memory_get_usage(true) / $limit >= 0.9) { + throw new RuntimeException(sprintf('Used %d%% of the current memory limit, please increase the limit to generate the whole documentation.', round(memory_get_usage(true) / $limit * 100))); + } + + return $this; + } + + /** + * Detects character set for the given text. + * + * @param string $text Text + * @return string + */ + private function detectCharset($text) + { + // One character set + if (1 === count($this->config->charset) && 'AUTO' !== $this->config->charset[0]) { + return $this->config->charset[0]; + } + + static $charsets = array(); + if (empty($charsets)) { + if (1 === count($this->config->charset) && 'AUTO' === $this->config->charset[0]) { + // Autodetection + $charsets = array( + 'Windows-1251', 'Windows-1252', 'ISO-8859-2', 'ISO-8859-1', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', + 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15' + ); + } else { + // More character sets + $charsets = $this->config->charset; + if (false !== ($key = array_search('WINDOWS-1250', $charsets))) { + // WINDOWS-1250 is not supported + $charsets[$key] = 'ISO-8859-2'; + } + } + // Only supported character sets + $charsets = array_intersect($charsets, mb_list_encodings()); + + // UTF-8 have to be first + array_unshift($charsets, 'UTF-8'); + } + + $charset = mb_detect_encoding($text, $charsets); + // The previous function can not handle WINDOWS-1250 and returns ISO-8859-2 instead + if ('ISO-8859-2' === $charset && preg_match('~[\x7F-\x9F\xBC]~', $text)) { + $charset = 'WINDOWS-1250'; + } + + return $charset; + } + + /** + * Converts text from given character set to UTF-8. + * + * @param string $text Text + * @param string $charset Character set + * @return string + */ + private function toUtf($text, $charset) + { + if ('UTF-8' === $charset) { + return $text; + } + + return @iconv($charset, 'UTF-8//TRANSLIT//IGNORE', $text); + } + + /** + * Checks if sitemap.xml is enabled. + * + * @return boolean + */ + private function isSitemapEnabled() + { + return !empty($this->config->baseUrl) && $this->templateExists('sitemap', 'optional'); + } + + /** + * Checks if opensearch.xml is enabled. + * + * @return boolean + */ + private function isOpensearchEnabled() + { + return !empty($this->config->googleCseId) && !empty($this->config->baseUrl) && $this->templateExists('opensearch', 'optional'); + } + + /** + * Checks if robots.txt is enabled. + * + * @return boolean + */ + private function isRobotsEnabled() + { + return !empty($this->config->baseUrl) && $this->templateExists('robots', 'optional'); + } + + /** + * Sorts methods by FQN. + * + * @param \ApiGen\ReflectionMethod $one + * @param \ApiGen\ReflectionMethod $two + * @return integer + */ + private function sortMethods(ReflectionMethod $one, ReflectionMethod $two) + { + return strcasecmp($one->getDeclaringClassName() . '::' . $one->getName(), $two->getDeclaringClassName() . '::' . $two->getName()); + } + + /** + * Sorts constants by FQN. + * + * @param \ApiGen\ReflectionConstant $one + * @param \ApiGen\ReflectionConstant $two + * @return integer + */ + private function sortConstants(ReflectionConstant $one, ReflectionConstant $two) + { + return strcasecmp(($one->getDeclaringClassName() ?: $one->getNamespaceName()) . '\\' . $one->getName(), ($two->getDeclaringClassName() ?: $two->getNamespaceName()) . '\\' . $two->getName()); + } + + /** + * Sorts functions by FQN. + * + * @param \ApiGen\ReflectionFunction $one + * @param \ApiGen\ReflectionFunction $two + * @return integer + */ + private function sortFunctions(ReflectionFunction $one, ReflectionFunction $two) + { + return strcasecmp($one->getNamespaceName() . '\\' . $one->getName(), $two->getNamespaceName() . '\\' . $two->getName()); + } + + /** + * Sorts functions by FQN. + * + * @param \ApiGen\ReflectionProperty $one + * @param \ApiGen\ReflectionProperty $two + * @return integer + */ + private function sortProperties(ReflectionProperty $one, ReflectionProperty $two) + { + return strcasecmp($one->getDeclaringClassName() . '::' . $one->getName(), $two->getDeclaringClassName() . '::' . $two->getName()); + } + + /** + * Returns list of element types. + * + * @return array + */ + private function getElementTypes() + { + static $types = array('classes', 'interfaces', 'traits', 'exceptions', 'constants', 'functions'); + return $types; + } + + /** + * Returns main filter. + * + * @return \Closure + */ + private function getMainFilter() + { + return function($element) { + return $element->isMain(); + }; + } + + /** + * Returns ZIP archive path. + * + * @return string + */ + private function getArchivePath() + { + $name = trim(sprintf('%s API documentation', $this->config->title)); + return $this->config->destination . DIRECTORY_SEPARATOR . Nette\Utils\Strings::webalize($name) . '.zip'; + } + + /** + * Returns filename relative path to the source directory. + * + * @param string $fileName + * @return string + * @throws \InvalidArgumentException If relative path could not be determined. + */ + public function getRelativePath($fileName) + { + if (isset($this->symlinks[$fileName])) { + $fileName = $this->symlinks[$fileName]; + } + foreach ($this->config->source as $source) { + if ($this->isPhar($source)) { + $source = $this->pharPath($source); + } + if (0 === strpos($fileName, $source)) { + return is_dir($source) ? str_replace('\\', '/', substr($fileName, strlen($source) + 1)) : basename($fileName); + } + } + + throw new InvalidArgumentException(sprintf('Could not determine "%s" relative path', $fileName)); + } + + /** + * Returns template directory. + * + * @return string + */ + private function getTemplateDir() + { + return dirname($this->config->templateConfig); + } + + /** + * Returns template path. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function getTemplatePath($name, $type = 'main') + { + return $this->getTemplateDir() . DIRECTORY_SEPARATOR . $this->config->template['templates'][$type][$name]['template']; + } + + /** + * Returns template filename. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function getTemplateFileName($name, $type = 'main') + { + return $this->config->destination . DIRECTORY_SEPARATOR . $this->config->template['templates'][$type][$name]['filename']; + } + + /** + * Checks if template exists. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function templateExists($name, $type = 'main') + { + return isset($this->config->template['templates'][$type][$name]); + } + + /** + * Checks if template exists and creates dir. + * + * @param string $name + * @throws \RuntimeException If template is not set. + */ + private function prepareTemplate($name) + { + if (!$this->templateExists($name)) { + throw new RuntimeException(sprintf('Template for "%s" is not set', $name)); + } + + $this->forceDir($this->getTemplateFileName($name)); + return $this; + } + + /** + * Returns list of all generated files. + * + * @return array + */ + private function getGeneratedFiles() + { + $files = array(); + + // Resources + foreach ($this->config->template['resources'] as $item) { + $path = $this->getTemplateDir() . DIRECTORY_SEPARATOR . $item; + if (is_dir($path)) { + $iterator = Nette\Utils\Finder::findFiles('*')->from($path)->getIterator(); + foreach ($iterator as $innerItem) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); + } + } else { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item; + } + } + + // Common files + foreach ($this->config->template['templates']['common'] as $item) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item; + } + + // Optional files + foreach ($this->config->template['templates']['optional'] as $optional) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $optional['filename']; + } + + // Main files + $masks = array_map(function($config) { + return preg_replace('~%[^%]*?s~', '*', $config['filename']); + }, $this->config->template['templates']['main']); + $filter = function($item) use ($masks) { + foreach ($masks as $mask) { + if (fnmatch($mask, $item->getFilename())) { + return true; + } + } + return false; + }; + + foreach (Nette\Utils\Finder::findFiles('*')->filter($filter)->from($this->config->destination) as $item) { + $files[] = $item->getPathName(); + } + + return $files; + } + + /** + * Ensures a directory is created. + * + * @param string $path Directory path + * @return string + */ + private function forceDir($path) + { + @mkdir(dirname($path), 0755, true); + return $path; + } + + /** + * Deletes a directory. + * + * @param string $path Directory path + * @return boolean + */ + private function deleteDir($path) + { + if (!is_dir($path)) { + return true; + } + + foreach (Nette\Utils\Finder::find('*')->from($path)->childFirst() as $item) { + if ($item->isDir()) { + if (!@rmdir($item)) { + return false; + } + } elseif ($item->isFile()) { + if (!@unlink($item)) { + return false; + } + } + } + if (!@rmdir($path)) { + return false; + } + + return true; + } +} diff --git a/apigen/ApiGen/IHelperSet.php b/apigen/ApiGen/IHelperSet.php new file mode 100644 index 00000000000..911c83820b1 --- /dev/null +++ b/apigen/ApiGen/IHelperSet.php @@ -0,0 +1,28 @@ +getConfig(); + self::$parsedClasses = $generator->getParsedClasses(); + self::$parsedConstants = $generator->getParsedConstants(); + self::$parsedFunctions = $generator->getParsedFunctions(); + } + + $this->reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + + $this->reflection = $reflection; + } + + /** + * Retrieves a property or method value. + * + * First tries the envelope object's property storage, then its methods + * and finally the inspected element reflection. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return $this->reflection->__get($name); + } + + /** + * Checks if the given property exists. + * + * First tries the envelope object's property storage, then its methods + * and finally the inspected element reflection. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]) || $this->reflection->__isset($name); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->reflection->getBroker(); + } + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->reflection->getName(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->reflection->getPrettyName(); + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return $this->reflection->isInternal(); + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return $this->reflection->isUserDefined(); + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return $this->reflection->isTokenized(); + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->reflection->getFileName(); + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + $startLine = $this->reflection->getStartLine(); + + if ($doc = $this->getDocComment()) { + $startLine -= substr_count($doc, "\n") + 1; + } + + return $startLine; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->reflection->getEndLine(); + } +} diff --git a/apigen/ApiGen/ReflectionClass.php b/apigen/ApiGen/ReflectionClass.php new file mode 100644 index 00000000000..2dd2c377cff --- /dev/null +++ b/apigen/ApiGen/ReflectionClass.php @@ -0,0 +1,1423 @@ +accessLevels) < 3) { + self::$methodAccessLevels = 0; + self::$propertyAccessLevels = 0; + + foreach (self::$config->accessLevels as $level) { + switch (strtolower($level)) { + case 'public': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PUBLIC; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PUBLIC; + break; + case 'protected': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PROTECTED; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PROTECTED; + break; + case 'private': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PRIVATE; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PRIVATE; + break; + default: + break; + } + } + } else { + self::$methodAccessLevels = null; + self::$propertyAccessLevels = null; + } + } + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + return $this->reflection->getShortName(); + } + + /** + * Returns modifiers. + * + * @return array + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the class is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return $this->reflection->isAbstract(); + } + + /** + * Returns if the class is final. + * + * @return boolean + */ + public function isFinal() + { + return $this->reflection->isFinal(); + } + + /** + * Returns if the class is an interface. + * + * @return boolean + */ + public function isInterface() + { + return $this->reflection->isInterface(); + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return $this->reflection->isException(); + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string $class Class name + * @return boolean + */ + public function isSubclassOf($class) + { + return $this->reflection->isSubclassOf($class); + } + + /** + * Returns visible methods. + * + * @return array + */ + public function getMethods() + { + if (null === $this->methods) { + $this->methods = $this->getOwnMethods(); + foreach ($this->reflection->getMethods(self::$methodAccessLevels) as $method) { + if (isset($this->methods[$method->getName()])) { + continue; + } + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $this->methods[$method->getName()] = $apiMethod; + } + } + } + return $this->methods; + } + + /** + * Returns visible methods declared by inspected class. + * + * @return array + */ + public function getOwnMethods() + { + if (null === $this->ownMethods) { + $this->ownMethods = array(); + foreach ($this->reflection->getOwnMethods(self::$methodAccessLevels) as $method) { + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $this->ownMethods[$method->getName()] = $apiMethod; + } + } + } + return $this->ownMethods; + } + + /** + * Returns visible magic methods. + * + * @return array + */ + public function getMagicMethods() + { + $methods = $this->getOwnMagicMethods(); + + $parent = $this->getParentClass(); + while ($parent) { + foreach ($parent->getOwnMagicMethods() as $method) { + if (isset($methods[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $method->isDocumented()) { + $methods[$method->getName()] = $method; + } + } + $parent = $parent->getParentClass(); + } + + foreach ($this->getTraits() as $trait) { + foreach ($trait->getOwnMagicMethods() as $method) { + if (isset($methods[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $method->isDocumented()) { + $methods[$method->getName()] = $method; + } + } + } + + return $methods; + } + + /** + * Returns visible magic methods declared by inspected class. + * + * @return array + */ + public function getOwnMagicMethods() + { + if (null === $this->ownMagicMethods) { + $this->ownMagicMethods = array(); + + if (!(self::$methodAccessLevels & InternalReflectionMethod::IS_PUBLIC) || false === $this->getDocComment()) { + return $this->ownMagicMethods; + } + + $annotations = $this->getAnnotation('method'); + if (null === $annotations) { + return $this->ownMagicMethods; + } + + foreach ($annotations as $annotation) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?(&)?\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*(.*|$)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $returnTypeHint, $returnsReference, $name, $args, $shortDescription) = $matches; + + $doc = $this->getDocComment(); + $tmp = $annotation; + if ($delimiter = strpos($annotation, "\n")) { + $tmp = substr($annotation, 0, $delimiter); + } + + $startLine = $this->getStartLine() + substr_count(substr($doc, 0, strpos($doc, $tmp)), "\n"); + $endLine = $startLine + substr_count($annotation, "\n"); + + $method = new ReflectionMethodMagic(null, self::$generator); + $method + ->setName($name) + ->setShortDescription(str_replace("\n", ' ', $shortDescription)) + ->setStartLine($startLine) + ->setEndLine($endLine) + ->setReturnsReference('&' === $returnsReference) + ->setDeclaringClass($this) + ->addAnnotation('return', $returnTypeHint); + + $this->ownMagicMethods[$name] = $method; + + $parameters = array(); + foreach (array_filter(preg_split('~\\s*,\\s*~', $args)) as $position => $arg) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?(&)?\\s*\\$(\\w+)(?:\\s*=\\s*(.*))?($)~s', $arg, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $passedByReference, $name, $defaultValueDefinition) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $parameter = new ReflectionParameterMagic(null, self::$generator); + $parameter + ->setName($name) + ->setPosition($position) + ->setTypeHint($typeHint) + ->setDefaultValueDefinition($defaultValueDefinition) + ->setUnlimited(false) + ->setPassedByReference('&' === $passedByReference) + ->setDeclaringFunction($method); + + $parameters[$name] = $parameter; + + $method->addAnnotation('param', ltrim(sprintf('%s $%s', $typeHint, $name))); + } + $method->setParameters($parameters); + } + } + return $this->ownMagicMethods; + } + + /** + * Returns visible methods declared by traits. + * + * @return array + */ + public function getTraitMethods() + { + $methods = array(); + foreach ($this->reflection->getTraitMethods(self::$methodAccessLevels) as $method) { + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $methods[$method->getName()] = $apiMethod; + } + } + return $methods; + } + + /** + * Returns a method reflection. + * + * @param string $name Method name + * @return \ApiGen\ReflectionMethod + * @throws \InvalidArgumentException If required method does not exist. + */ + public function getMethod($name) + { + if ($this->hasMethod($name)) { + return $this->methods[$name]; + } + + throw new InvalidArgumentException(sprintf('Method %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns visible properties. + * + * @return array + */ + public function getProperties() + { + if (null === $this->properties) { + $this->properties = $this->getOwnProperties(); + foreach ($this->reflection->getProperties(self::$propertyAccessLevels) as $property) { + if (isset($this->properties[$property->getName()])) { + continue; + } + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $this->properties[$property->getName()] = $apiProperty; + } + } + } + return $this->properties; + } + + /** + * Returns visible magic properties. + * + * @return array + */ + public function getMagicProperties() + { + $properties = $this->getOwnMagicProperties(); + + $parent = $this->getParentClass(); + while ($parent) { + foreach ($parent->getOwnMagicProperties() as $property) { + if (isset($properties[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $property->isDocumented()) { + $properties[$property->getName()] = $property; + } + } + $parent = $parent->getParentClass(); + } + + foreach ($this->getTraits() as $trait) { + foreach ($trait->getOwnMagicProperties() as $property) { + if (isset($properties[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $property->isDocumented()) { + $properties[$property->getName()] = $property; + } + } + } + + return $properties; + } + + /** + * Returns visible properties declared by inspected class. + * + * @return array + */ + public function getOwnProperties() + { + if (null === $this->ownProperties) { + $this->ownProperties = array(); + foreach ($this->reflection->getOwnProperties(self::$propertyAccessLevels) as $property) { + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $this->ownProperties[$property->getName()] = $apiProperty; + } + } + } + return $this->ownProperties; + } + + /** + * Returns visible properties magicly declared by inspected class. + * + * @return array + */ + public function getOwnMagicProperties() + { + if (null === $this->ownMagicProperties) { + $this->ownMagicProperties = array(); + + if (!(self::$propertyAccessLevels & InternalReflectionProperty::IS_PUBLIC) || false === $this->getDocComment()) { + return $this->ownMagicProperties; + } + + foreach (array('property', 'property-read', 'property-write') as $annotationName) { + $annotations = $this->getAnnotation($annotationName); + if (null === $annotations) { + continue; + } + + foreach ($annotations as $annotation) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?\\$(\\w+)(?:\\s+(.*))?($)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $name, $shortDescription) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $doc = $this->getDocComment(); + $tmp = $annotation; + if ($delimiter = strpos($annotation, "\n")) { + $tmp = substr($annotation, 0, $delimiter); + } + + $startLine = $this->getStartLine() + substr_count(substr($doc, 0, strpos($doc, $tmp)), "\n"); + $endLine = $startLine + substr_count($annotation, "\n"); + + $magicProperty = new ReflectionPropertyMagic(null, self::$generator); + $magicProperty + ->setName($name) + ->setTypeHint($typeHint) + ->setShortDescription(str_replace("\n", ' ', $shortDescription)) + ->setStartLine($startLine) + ->setEndLine($endLine) + ->setReadOnly('property-read' === $annotationName) + ->setWriteOnly('property-write' === $annotationName) + ->setDeclaringClass($this) + ->addAnnotation('var', $typeHint); + + $this->ownMagicProperties[$name] = $magicProperty; + } + } + } + + return $this->ownMagicProperties; + } + + /** + * Returns visible properties declared by traits. + * + * @return array + */ + public function getTraitProperties() + { + $properties = array(); + foreach ($this->reflection->getTraitProperties(self::$propertyAccessLevels) as $property) { + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $properties[$property->getName()] = $apiProperty; + } + } + return $properties; + } + + /** + * Returns a method property. + * + * @param string $name Method name + * @return \ApiGen\ReflectionProperty + * @throws \InvalidArgumentException If required property does not exist. + */ + public function getProperty($name) + { + if ($this->hasProperty($name)) { + return $this->properties[$name]; + } + + throw new InvalidArgumentException(sprintf('Property %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns visible properties. + * + * @return array + */ + public function getConstants() + { + if (null === $this->constants) { + $this->constants = array(); + foreach ($this->reflection->getConstantReflections() as $constant) { + $apiConstant = new ReflectionConstant($constant, self::$generator); + if (!$this->isDocumented() || $apiConstant->isDocumented()) { + $this->constants[$constant->getName()] = $apiConstant; + } + } + } + + return $this->constants; + } + + /** + * Returns constants declared by inspected class. + * + * @return array + */ + public function getOwnConstants() + { + if (null === $this->ownConstants) { + $this->ownConstants = array(); + $className = $this->reflection->getName(); + foreach ($this->getConstants() as $constantName => $constant) { + if ($className === $constant->getDeclaringClassName()) { + $this->ownConstants[$constantName] = $constant; + } + } + } + return $this->ownConstants; + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + * @throws \InvalidArgumentException If required constant does not exist. + */ + public function getConstantReflection($name) + { + if (null === $this->constants) { + $this->getConstants(); + } + + if (isset($this->constants[$name])) { + return $this->constants[$name]; + } + + throw new InvalidArgumentException(sprintf('Constant %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + */ + public function getConstant($name) + { + return $this->getConstantReflection($name); + } + + /** + * Checks if there is a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasConstant($constantName) + { + if (null === $this->constants) { + $this->getConstants(); + } + + return isset($this->constants[$constantName]); + } + + /** + * Checks if there is a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasOwnConstant($constantName) + { + if (null === $this->ownConstants) { + $this->getOwnConstants(); + } + + return isset($this->ownConstants[$constantName]); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + * @throws \InvalidArgumentException If required constant does not exist. + */ + public function getOwnConstantReflection($name) + { + if (null === $this->ownConstants) { + $this->getOwnConstants(); + } + + if (isset($this->ownConstants[$name])) { + return $this->ownConstants[$name]; + } + + throw new InvalidArgumentException(sprintf('Constant %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + */ + public function getOwnConstant($name) + { + return $this->getOwnConstantReflection($name); + } + + /** + * Returns a parent class reflection encapsulated by this class. + * + * @return \ApiGen\ReflectionClass + */ + public function getParentClass() + { + if ($className = $this->reflection->getParentClassName()) { + return self::$parsedClasses[$className]; + } + return $className; + } + + /** + * Returns the parent class name. + * + * @return string|null + */ + public function getParentClassName() + { + return $this->reflection->getParentClassName(); + } + + /** + * Returns all parent classes reflections encapsulated by this class. + * + * @return array + */ + public function getParentClasses() + { + if (null === $this->parentClasses) { + $classes = self::$parsedClasses; + $this->parentClasses = array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getParentClasses()); + } + return $this->parentClasses; + } + + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + return $this->reflection->getParentClassNameList(); + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + */ + public function implementsInterface($interface) + { + return $this->reflection->implementsInterface($interface); + } + + /** + * Returns all interface reflections encapsulated by this class. + * + * @return array + */ + public function getInterfaces() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getInterfaces()); + } + + /** + * Returns interface names. + * + * @return array + */ + public function getInterfaceNames() + { + return $this->reflection->getInterfaceNames(); + } + + /** + * Returns all interfaces implemented by the inspected class and not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getOwnInterfaces()); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return $this->reflection->getOwnInterfaceNames(); + } + + /** + * Returns all traits reflections encapsulated by this class. + * + * @return array + */ + public function getTraits() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getTraits()); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + return $this->reflection->getTraitNames(); + } + + /** + * Returns names of traits used by this class an not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return $this->reflection->getOwnTraitNames(); + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return $this->reflection->getTraitAliases(); + } + + /** + * Returns all traits used by the inspected class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getOwnTraits()); + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return $this->reflection->isTrait(); + } + + /** + * Returns if the class uses a particular trait. + * + * @param string $trait Trait name + * @return boolean + */ + public function usesTrait($trait) + { + return $this->reflection->usesTrait($trait); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubClasses() + { + $subClasses = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($name === $class->getParentClassName()) { + $subClasses[] = $class; + } + } + return $subClasses; + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubClasses() + { + $subClasses = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($name !== $class->getParentClassName() && $class->isSubclassOf($name)) { + $subClasses[] = $class; + } + } + return $subClasses; + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $implementers = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if (in_array($name, $class->getOwnInterfaceNames())) { + $implementers[] = $class; + } + } + return $implementers; + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $implementers = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($class->implementsInterface($name) && !in_array($name, $class->getOwnInterfaceNames())) { + $implementers[] = $class; + } + } + return $implementers; + } + + /** + * Returns reflections of classes directly using this trait. + * + * @return array + */ + public function getDirectUsers() + { + if (!$this->isTrait()) { + return array(); + } + + $users = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + + if (in_array($name, $class->getOwnTraitNames())) { + $users[] = $class; + } + } + return $users; + } + + /** + * Returns reflections of classes indirectly using this trait. + * + * @return array + */ + public function getIndirectUsers() + { + if (!$this->isTrait()) { + return array(); + } + + $users = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($class->usesTrait($name) && !in_array($name, $class->getOwnTraitNames())) { + $users[] = $class; + } + } + return $users; + } + + /** + * Returns an array of inherited methods from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMethods() + { + $methods = array(); + $allMethods = array_flip(array_map(function($method) { + return $method->getName(); + }, $this->getOwnMethods())); + + foreach (array_merge($this->getParentClasses(), $this->getInterfaces()) as $class) { + $inheritedMethods = array(); + foreach ($class->getOwnMethods() as $method) { + if (!array_key_exists($method->getName(), $allMethods) && !$method->isPrivate()) { + $inheritedMethods[$method->getName()] = $method; + $allMethods[$method->getName()] = null; + } + } + + if (!empty($inheritedMethods)) { + ksort($inheritedMethods); + $methods[$class->getName()] = array_values($inheritedMethods); + } + } + + return $methods; + } + + /** + * Returns an array of inherited magic methods from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMagicMethods() + { + $methods = array(); + $allMethods = array_flip(array_map(function($method) { + return $method->getName(); + }, $this->getOwnMagicMethods())); + + foreach (array_merge($this->getParentClasses(), $this->getInterfaces()) as $class) { + $inheritedMethods = array(); + foreach ($class->getOwnMagicMethods() as $method) { + if (!array_key_exists($method->getName(), $allMethods)) { + $inheritedMethods[$method->getName()] = $method; + $allMethods[$method->getName()] = null; + } + } + + if (!empty($inheritedMethods)) { + ksort($inheritedMethods); + $methods[$class->getName()] = array_values($inheritedMethods); + } + } + + return $methods; + } + + /** + * Returns an array of used methods from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMethods() + { + $usedMethods = array(); + foreach ($this->getMethods() as $method) { + if (null === $method->getDeclaringTraitName() || $this->getName() === $method->getDeclaringTraitName()) { + continue; + } + + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['method'] = $method; + if (null !== $method->getOriginalName() && $method->getName() !== $method->getOriginalName()) { + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['aliases'][$method->getName()] = $method; + } + } + + // Sort + array_walk($usedMethods, function(&$methods) { + ksort($methods); + array_walk($methods, function(&$aliasedMethods) { + if (!isset($aliasedMethods['aliases'])) { + $aliasedMethods['aliases'] = array(); + } + ksort($aliasedMethods['aliases']); + }); + }); + + return $usedMethods; + } + + /** + * Returns an array of used magic methods from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMagicMethods() + { + $usedMethods = array(); + + foreach ($this->getMagicMethods() as $method) { + if (null === $method->getDeclaringTraitName() || $this->getName() === $method->getDeclaringTraitName()) { + continue; + } + + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['method'] = $method; + } + + // Sort + array_walk($usedMethods, function(&$methods) { + ksort($methods); + array_walk($methods, function(&$aliasedMethods) { + if (!isset($aliasedMethods['aliases'])) { + $aliasedMethods['aliases'] = array(); + } + ksort($aliasedMethods['aliases']); + }); + }); + + return $usedMethods; + } + + /** + * Returns an array of inherited constants from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedConstants() + { + return array_filter( + array_map( + function(ReflectionClass $class) { + $reflections = $class->getOwnConstants(); + ksort($reflections); + return $reflections; + }, + array_merge($this->getParentClasses(), $this->getInterfaces()) + ) + ); + } + + /** + * Returns an array of inherited properties from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnProperties())); + + foreach ($this->getParentClasses() as $class) { + $inheritedProperties = array(); + foreach ($class->getOwnProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties) && !$property->isPrivate()) { + $inheritedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($inheritedProperties)) { + ksort($inheritedProperties); + $properties[$class->getName()] = array_values($inheritedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of inherited magic properties from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMagicProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnMagicProperties())); + + foreach ($this->getParentClasses() as $class) { + $inheritedProperties = array(); + foreach ($class->getOwnMagicProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $inheritedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($inheritedProperties)) { + ksort($inheritedProperties); + $properties[$class->getName()] = array_values($inheritedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of used properties from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnProperties())); + + foreach ($this->getTraits() as $trait) { + $usedProperties = array(); + foreach ($trait->getOwnProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $usedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($usedProperties)) { + ksort($usedProperties); + $properties[$trait->getName()] = array_values($usedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of used magic properties from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMagicProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnMagicProperties())); + + foreach ($this->getTraits() as $trait) { + $usedProperties = array(); + foreach ($trait->getOwnMagicProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $usedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($usedProperties)) { + ksort($usedProperties); + $properties[$trait->getName()] = array_values($usedProperties); + } + } + + return $properties; + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasProperty($propertyName) + { + if (null === $this->properties) { + $this->getProperties(); + } + + return isset($this->properties[$propertyName]); + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasOwnProperty($propertyName) + { + if (null === $this->ownProperties) { + $this->getOwnProperties(); + } + + return isset($this->ownProperties[$propertyName]); + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasTraitProperty($propertyName) + { + $properties = $this->getTraitProperties(); + return isset($properties[$propertyName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasMethod($methodName) + { + if (null === $this->methods) { + $this->getMethods(); + } + + return isset($this->methods[$methodName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasOwnMethod($methodName) + { + if (null === $this->ownMethods) { + $this->getOwnMethods(); + } + + return isset($this->ownMethods[$methodName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasTraitMethod($methodName) + { + $methods = $this->getTraitMethods(); + return isset($methods[$methodName]); + } + + /** + * Returns if the class is valid. + * + * @return boolean + */ + public function isValid() + { + if ($this->reflection instanceof TokenReflection\Invalid\ReflectionClass) { + return false; + } + + return true; + } + + /** + * Returns if the class should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/apigen/ApiGen/ReflectionConstant.php b/apigen/ApiGen/ReflectionConstant.php new file mode 100644 index 00000000000..c7993aa39ce --- /dev/null +++ b/apigen/ApiGen/ReflectionConstant.php @@ -0,0 +1,145 @@ +reflection->getShortName(); + } + + /** + * Returns constant type hint. + * + * @return string + */ + public function getTypeHint() + { + if ($annotations = $this->getAnnotation('var')) { + list($types) = preg_split('~\s+|$~', $annotations[0], 2); + if (!empty($types)) { + return $types; + } + } + + try { + $type = gettype($this->getValue()); + if ('null' !== strtolower($type)) { + return $type; + } + } catch (\Exception $e) { + // Nothing + } + + return 'mixed'; + } + + /** + * Returns the constant declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the name of the declaring class. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns the constant value. + * + * @return mixed + */ + public function getValue() + { + return $this->reflection->getValue(); + } + + /** + * Returns the constant value definition. + * + * @return string + */ + public function getValueDefinition() + { + return $this->reflection->getValueDefinition(); + } + + /** + * Returns if the constant is valid. + * + * @return boolean + */ + public function isValid() + { + if ($this->reflection instanceof \TokenReflection\Invalid\ReflectionConstant) { + return false; + } + + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } + + /** + * Returns if the constant should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented() && null === $this->reflection->getDeclaringClassName()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/apigen/ApiGen/ReflectionElement.php b/apigen/ApiGen/ReflectionElement.php new file mode 100644 index 00000000000..8f12485a3dd --- /dev/null +++ b/apigen/ApiGen/ReflectionElement.php @@ -0,0 +1,373 @@ +reflection->getExtension(); + return null === $extension ? null : new ReflectionExtension($extension, self::$generator); + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return $this->reflection->getExtensionName(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->reflection->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->reflection->getEndPosition(); + } + + /** + * Returns if the element belongs to main project. + * + * @return boolean + */ + public function isMain() + { + return empty(self::$config->main) || 0 === strpos($this->getName(), self::$config->main); + } + + /** + * Returns if the element should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = $this->reflection->isTokenized() || $this->reflection->isInternal(); + + if ($this->isDocumented) { + if (!self::$config->php && $this->reflection->isInternal()) { + $this->isDocumented = false; + } elseif (!self::$config->deprecated && $this->reflection->isDeprecated()) { + $this->isDocumented = false; + } elseif (!self::$config->internal && ($internal = $this->reflection->getAnnotation('internal')) && empty($internal[0])) { + $this->isDocumented = false; + } elseif (count($this->reflection->getAnnotation('ignore')) > 0) { + $this->isDocumented = false; + } + } + } + + return $this->isDocumented; + } + + /** + * Returns if the element is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + if ($this->reflection->isDeprecated()) { + return true; + } + + if (($this instanceof ReflectionMethod || $this instanceof ReflectionProperty || $this instanceof ReflectionConstant) + && $class = $this->getDeclaringClass() + ) { + return $class->isDeprecated(); + } + + return false; + } + + /** + * Returns if the element is in package. + * + * @return boolean + */ + public function inPackage() + { + return '' !== $this->getPackageName(); + } + + /** + * Returns element package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + static $packages = array(); + + if ($package = $this->getAnnotation('package')) { + $packageName = preg_replace('~\s+.*~s', '', $package[0]); + if (empty($packageName)) { + return ''; + } + + if ($subpackage = $this->getAnnotation('subpackage')) { + $subpackageName = preg_replace('~\s+.*~s', '', $subpackage[0]); + if (empty($subpackageName)) { + // Do nothing + } elseif (0 === strpos($subpackageName, $packageName)) { + $packageName = $subpackageName; + } else { + $packageName .= '\\' . $subpackageName; + } + } + $packageName = strtr($packageName, '._/', '\\\\\\'); + + $lowerPackageName = strtolower($packageName); + if (!isset($packages[$lowerPackageName])) { + $packages[$lowerPackageName] = $packageName; + } + + return $packages[$lowerPackageName]; + } + + return ''; + } + + /** + * Returns element package name (including subpackage name). + * + * For internal elements returns "PHP", for elements in global space returns "None". + * + * @return string + */ + public function getPseudoPackageName() + { + if ($this->isInternal()) { + return 'PHP'; + } + + return $this->getPackageName() ?: 'None'; + } + + /** + * Returns if the element is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return '' !== $this->getNamespaceName(); + } + + /** + * Returns element namespace name. + * + * @return string + */ + public function getNamespaceName() + { + static $namespaces = array(); + + $namespaceName = $this->reflection->getNamespaceName(); + + if (!$namespaceName) { + return $namespaceName; + } + + $lowerNamespaceName = strtolower($namespaceName); + if (!isset($namespaces[$lowerNamespaceName])) { + $namespaces[$lowerNamespaceName] = $namespaceName; + } + + return $namespaces[$lowerNamespaceName]; + } + + /** + * Returns element namespace name. + * + * For internal elements returns "PHP", for elements in global space returns "None". + * + * @return string + */ + public function getPseudoNamespaceName() + { + return $this->isInternal() ? 'PHP' : $this->getNamespaceName() ?: 'None'; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->reflection->getNamespaceAliases(); + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + $short = $this->reflection->getAnnotation(\TokenReflection\ReflectionAnnotation::SHORT_DESCRIPTION); + if (!empty($short)) { + return $short; + } + + if ($this instanceof ReflectionProperty || $this instanceof ReflectionConstant) { + $var = $this->getAnnotation('var'); + list(, $short) = preg_split('~\s+|$~', $var[0], 2); + } + + return $short; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + $short = $this->getShortDescription(); + $long = $this->reflection->getAnnotation(\TokenReflection\ReflectionAnnotation::LONG_DESCRIPTION); + + if (!empty($long)) { + $short .= "\n\n" . $long; + } + + return $short; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + return $this->reflection->getDocComment(); + } + + /** + * Returns reflection element annotations. + * + * Removes the short and long description. + * + * In case of classes, functions and constants, @package, @subpackage, @author and @license annotations + * are added from declaring files if not already present. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + static $fileLevel = array('package' => true, 'subpackage' => true, 'author' => true, 'license' => true, 'copyright' => true); + + $annotations = $this->reflection->getAnnotations(); + unset($annotations[\TokenReflection\ReflectionAnnotation::SHORT_DESCRIPTION]); + unset($annotations[\TokenReflection\ReflectionAnnotation::LONG_DESCRIPTION]); + + if ($this->reflection instanceof \TokenReflection\ReflectionClass || $this->reflection instanceof \TokenReflection\ReflectionFunction || ($this->reflection instanceof \TokenReflection\ReflectionConstant && null === $this->reflection->getDeclaringClassName())) { + foreach ($this->reflection->getFileReflection()->getAnnotations() as $name => $value) { + if (isset($fileLevel[$name]) && empty($annotations[$name])) { + $annotations[$name] = $value; + } + } + } + + $this->annotations = $annotations; + } + + return $this->annotations; + } + + /** + * Returns reflection element annotation. + * + * @param string $annotation Annotation name + * @return array + */ + public function getAnnotation($annotation) + { + $annotations = $this->getAnnotations(); + return isset($annotations[$annotation]) ? $annotations[$annotation] : null; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $annotation Annotation name + * @return boolean + */ + public function hasAnnotation($annotation) + { + $annotations = $this->getAnnotations(); + return isset($annotations[$annotation]); + } + + /** + * Adds element annotation. + * + * @param string $annotation Annotation name + * @param string $value Annotation value + * @return \ApiGen\ReflectionElement + */ + public function addAnnotation($annotation, $value) + { + if (null === $this->annotations) { + $this->getAnnotations(); + } + $this->annotations[$annotation][] = $value; + + return $this; + } +} diff --git a/apigen/ApiGen/ReflectionExtension.php b/apigen/ApiGen/ReflectionExtension.php new file mode 100644 index 00000000000..1d636bcdaff --- /dev/null +++ b/apigen/ApiGen/ReflectionExtension.php @@ -0,0 +1,135 @@ +reflection->getClass($name); + if (null === $class) { + return null; + } + if (isset(self::$parsedClasses[$name])) { + return self::$parsedClasses[$name]; + } + return new ReflectionClass($class, self::$generator); + } + + /** + * Returns classes defined by this extension. + * + * @return array + */ + public function getClasses() + { + $generator = self::$generator; + $classes = self::$parsedClasses; + return array_map(function(TokenReflection\IReflectionClass $class) use ($generator, $classes) { + return isset($classes[$class->getName()]) ? $classes[$class->getName()] : new ReflectionClass($class, $generator); + }, $this->reflection->getClasses()); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant|null + */ + public function getConstant($name) + { + return $this->getConstantReflection($name); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant|null + */ + public function getConstantReflection($name) + { + $constant = $this->reflection->getConstantReflection($name); + return null === $constant ? null : new ReflectionConstant($constant, self::$generator); + } + + /** + * Returns reflections of defined constants. + * + * @return array + */ + public function getConstants() + { + return $this->getConstantReflections(); + } + + /** + * Returns reflections of defined constants. + * + * @return array + */ + public function getConstantReflections() + { + $generator = self::$generator; + return array_map(function(TokenReflection\IReflectionConstant $constant) use ($generator) { + return new ReflectionConstant($constant, $generator); + }, $this->reflection->getConstantReflections()); + } + + /** + * Returns a function reflection. + * + * @param string $name Function name + * @return \ApiGen\ReflectionFunction + */ + public function getFunction($name) + { + $function = $this->reflection->getFunction($name); + return null === $function ? null : new ReflectionFunction($function, self::$generator); + } + + /** + * Returns functions defined by this extension. + * + * @return array + */ + public function getFunctions() + { + $generator = self::$generator; + return array_map(function(TokenReflection\IReflectionFunction $function) use ($generator) { + return new ReflectionFunction($function, $generator); + }, $this->reflection->getFunctions()); + } + + /** + * Returns names of functions defined by this extension. + * + * @return array + */ + public function getFunctionNames() + { + return $this->reflection->getFunctionNames(); + } +} diff --git a/apigen/ApiGen/ReflectionFunction.php b/apigen/ApiGen/ReflectionFunction.php new file mode 100644 index 00000000000..75f2e4038d8 --- /dev/null +++ b/apigen/ApiGen/ReflectionFunction.php @@ -0,0 +1,64 @@ +reflection instanceof \TokenReflection\Invalid\ReflectionFunction) { + return false; + } + + return true; + } + + /** + * Returns if the function should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/apigen/ApiGen/ReflectionFunctionBase.php b/apigen/ApiGen/ReflectionFunctionBase.php new file mode 100644 index 00000000000..a86326fd9af --- /dev/null +++ b/apigen/ApiGen/ReflectionFunctionBase.php @@ -0,0 +1,151 @@ +reflection->getShortName(); + } + + /** + * Returns if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return $this->reflection->returnsReference(); + } + + /** + * Returns a list of function/method parameters. + * + * @return array + */ + public function getParameters() + { + if (null === $this->parameters) { + $generator = self::$generator; + $this->parameters = array_map(function(TokenReflection\IReflectionParameter $parameter) use ($generator) { + return new ReflectionParameter($parameter, $generator); + }, $this->reflection->getParameters()); + + $annotations = $this->getAnnotation('param'); + if (null !== $annotations) { + foreach ($annotations as $position => $annotation) { + if (isset($parameters[$position])) { + // Standard parameter + continue; + } + + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?\\$(\\w+),\\.{3}(?:\\s+(.*))?($)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $name) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $parameter = new ReflectionParameterMagic(null, self::$generator); + $parameter + ->setName($name) + ->setPosition($position) + ->setTypeHint($typeHint) + ->setDefaultValueDefinition(null) + ->setUnlimited(true) + ->setPassedByReference(false) + ->setDeclaringFunction($this); + + $this->parameters[$position] = $parameter; + } + } + } + + return $this->parameters; + } + + /** + * Returns a particular function/method parameter. + * + * @param integer|string $parameterName Parameter name or position + * @return \ApiGen\ReflectionParameter + * @throws \InvalidArgumentException If there is no parameter of the given name. + * @throws \InvalidArgumentException If there is no parameter at the given position. + */ + public function getParameter($parameterName) + { + $parameters = $this->getParameters(); + + if (is_numeric($parameterName)) { + if (isset($parameters[$parameterName])) { + return $parameters[$parameterName]; + } + + throw new InvalidArgumentException(sprintf('There is no parameter at position "%d" in function/method "%s"', $parameterName, $this->getName()), Exception\Runtime::DOES_NOT_EXIST); + } else { + foreach ($parameters as $parameter) { + if ($parameter->getName() === $parameterName) { + return $parameter; + } + } + + throw new InvalidArgumentException(sprintf('There is no parameter "%s" in function/method "%s"', $parameterName, $this->getName()), Exception\Runtime::DOES_NOT_EXIST); + } + } + + /** + * Returns the number of parameters. + * + * @return integer + */ + public function getNumberOfParameters() + { + return $this->reflection->getNumberOfParameters(); + } + + /** + * Returns the number of required parameters. + * + * @return integer + */ + public function getNumberOfRequiredParameters() + { + return $this->reflection->getNumberOfRequiredParameters(); + } +} diff --git a/apigen/ApiGen/ReflectionMethod.php b/apigen/ApiGen/ReflectionMethod.php new file mode 100644 index 00000000000..007935ccd9f --- /dev/null +++ b/apigen/ApiGen/ReflectionMethod.php @@ -0,0 +1,250 @@ +reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns method modifiers. + * + * @return integer + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the method is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return $this->reflection->isAbstract(); + } + + /** + * Returns if the method is final. + * + * @return boolean + */ + public function isFinal() + { + return $this->reflection->isFinal(); + } + + /** + * Returns if the method is private. + * + * @return boolean + */ + public function isPrivate() + { + return $this->reflection->isPrivate(); + } + + /** + * Returns if the method is protected. + * + * @return boolean + */ + public function isProtected() + { + return $this->reflection->isProtected(); + } + + /** + * Returns if the method is public. + * + * @return boolean + */ + public function isPublic() + { + return $this->reflection->isPublic(); + } + + /** + * Returns if the method is static. + * + * @return boolean + */ + public function isStatic() + { + return $this->reflection->isStatic(); + } + + /** + * Returns if the method is a constructor. + * + * @return boolean + */ + public function isConstructor() + { + return $this->reflection->isConstructor(); + } + + /** + * Returns if the method is a destructor. + * + * @return boolean + */ + public function isDestructor() + { + return $this->reflection->isDestructor(); + } + + /** + * Returns the method declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + $traitName = $this->reflection->getDeclaringTraitName(); + return null === $traitName ? null : self::$parsedClasses[$traitName]; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->reflection->getDeclaringTraitName(); + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getImplementedMethod() + { + foreach ($this->getDeclaringClass()->getOwnInterfaces() as $interface) { + if ($interface->hasMethod($this->getName())) { + return $interface->getMethod($this->getName()); + } + } + + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOverriddenMethod() + { + $parent = $this->getDeclaringClass()->getParentClass(); + if (null === $parent) { + return null; + } + + foreach ($parent->getMethods() as $method) { + if ($this->getName() === $method->getName()) { + if (!$method->isPrivate() && !$method->isAbstract()) { + return $method; + } else { + return null; + } + } + } + + return null; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string|null + */ + public function getOriginalName() + { + return $this->reflection->getOriginalName(); + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return integer|null + */ + public function getOriginalModifiers() + { + return $this->reflection->getOriginalModifiers(); + } + + /** + * Returns the original method when importing from a trait. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOriginal() + { + $originalName = $this->reflection->getOriginalName(); + return null === $originalName ? null : self::$parsedClasses[$this->reflection->getOriginal()->getDeclaringClassName()]->getMethod($originalName); + } + + /** + * Returns if the method is valid. + * + * @return boolean + */ + public function isValid() + { + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } +} diff --git a/apigen/ApiGen/ReflectionMethodMagic.php b/apigen/ApiGen/ReflectionMethodMagic.php new file mode 100644 index 00000000000..05b4514f746 --- /dev/null +++ b/apigen/ApiGen/ReflectionMethodMagic.php @@ -0,0 +1,729 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets method name. + * + * @param string $name + * @return \Apigen\ReflectionMethodMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + + } + + /** + * Sets short description. + * + * @param string $shortDescription + * @return \Apigen\ReflectionMethodMagic + */ + public function setShortDescription($shortDescription) + { + $this->shortDescription = (string) $shortDescription; + return $this; + } + + /** + * Sets start line. + * + * @param integer $startLine + * @return \Apigen\ReflectionMethodMagic + */ + public function setStartLine($startLine) + { + $this->startLine = (int) $startLine; + return $this; + } + + /** + * Sets end line. + * + * @param integer $endLine + * @return \Apigen\ReflectionMethodMagic + */ + public function setEndLine($endLine) + { + $this->endLine = (int) $endLine; + return $this; + } + + /** + * Sets if the method returns reference. + * + * @param boolean $returnsReference + * @return \Apigen\ReflectionMethodMagic + */ + public function setReturnsReference($returnsReference) + { + $this->returnsReference = (bool) $returnsReference; + return $this; + } + + /** + * Sets parameters. + * + * @param array $parameters + * @return \Apigen\ReflectionMethodMagic + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + return $this; + } + + /** + * Sets declaring class. + * + * @param \ApiGen\ReflectionClass $declaringClass + * @return \ApiGen\ReflectionMethodMagic + */ + public function setDeclaringClass(ReflectionClass $declaringClass) + { + $this->declaringClass = $declaringClass; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringClass->getBroker(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->declaringClass->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->declaringClass->getEndPosition(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + return $this->shortDescription; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + return $this->shortDescription; + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return $this->startLine; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->endLine; + } + + /** + * Returns if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return $this->returnsReference; + } + + /** + * Returns if the property is magic. + * + * @return boolean + */ + public function isMagic() + { + return true; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + return $this->name; + } + + /** + * Returns the PHP extension reflection. + * + * @return \ApiGen\ReflectionExtension|null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns if the method should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = self::$config->deprecated || !$this->isDeprecated(); + } + + return $this->isDocumented; + } + + /** + * Returns if the property is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return $this->declaringClass->isDeprecated(); + } + + /** + * Returns property package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + return $this->declaringClass->getPackageName(); + } + + /** + * Returns property namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->declaringClass->getNamespaceName(); + } + + /** + * Returns property annotations. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + $this->annotations = array(); + } + return $this->annotations; + } + + /** + * Returns the method declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringClass; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClass->getName(); + } + + /** + * Returns method modifiers. + * + * @return integer + */ + public function getModifiers() + { + return InternalReflectionMethod::IS_PUBLIC; + } + + /** + * Returns if the method is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return false; + } + + /** + * Returns if the method is final. + * + * @return boolean + */ + public function isFinal() + { + return false; + } + + /** + * Returns if the method is private. + * + * @return boolean + */ + public function isPrivate() + { + return false; + } + + /** + * Returns if the method is protected. + * + * @return boolean + */ + public function isProtected() + { + return false; + } + + /** + * Returns if the method is public. + * + * @return boolean + */ + public function isPublic() + { + return true; + } + + /** + * Returns if the method is static. + * + * @return boolean + */ + public function isStatic() + { + return false; + } + + /** + * Returns if the property is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the method is a constructor. + * + * @return boolean + */ + public function isConstructor() + { + return false; + } + + /** + * Returns if the method is a destructor. + * + * @return boolean + */ + public function isDestructor() + { + return false; + } + + /** + * Returns the method declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + return $this->declaringClass->isTrait() ? $this->declaringClass : null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + if ($declaringTrait = $this->getDeclaringTrait()) { + return $declaringTrait->getName(); + } + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getImplementedMethod() + { + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOverriddenMethod() + { + $parent = $this->declaringClass->getParentClass(); + if (null === $parent) { + return null; + } + + foreach ($parent->getMagicMethods() as $method) { + if ($this->name === $method->getName()) { + return $method; + } + } + + return null; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string|null + */ + public function getOriginalName() + { + return $this->getName(); + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return integer|null + */ + public function getOriginalModifiers() + { + return $this->getModifiers(); + } + + /** + * Returns the original method when importing from a trait. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOriginal() + { + return null; + } + + /** + * Returns a list of method parameters. + * + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Returns the number of parameters. + * + * @return integer + */ + public function getNumberOfParameters() + { + return count($this->parameters); + } + + /** + * Returns the number of required parameters. + * + * @return integer + */ + public function getNumberOfRequiredParameters() + { + $count = 0; + array_walk($this->parameters, function(ReflectionParameter $parameter) use (&$count) { + if (!$parameter->isOptional()) { + $count++; + } + }); + return $count; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->declaringClass->getNamespaceAliases(); + } + + /** + * Returns an property pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::%s()', $this->declaringClass->getName(), $this->name); + } + + /** + * Returns the file name the method is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringClass->getFileName(); + } + + /** + * Returns if the method is user defined. + + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the method comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + $docComment = "/**\n"; + + if (!empty($this->shortDescription)) { + $docComment .= $this->shortDescription . "\n\n"; + } + + if ($annotations = $this->getAnnotation('param')) { + foreach ($annotations as $annotation) { + $docComment .= sprintf("@param %s\n", $annotation); + } + } + + if ($annotations = $this->getAnnotation('return')) { + foreach ($annotations as $annotation) { + $docComment .= sprintf("@return %s\n", $annotation); + } + } + + $docComment .= "*/\n"; + + return $docComment; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + $annotations = $this->getAnnotations(); + return array_key_exists($name, $annotations); + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + public function getAnnotation($name) + { + $annotations = $this->getAnnotations(); + if (array_key_exists($name, $annotations)) { + return $annotations[$name]; + } + return null; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/apigen/ApiGen/ReflectionParameter.php b/apigen/ApiGen/ReflectionParameter.php new file mode 100644 index 00000000000..9157d71b2f4 --- /dev/null +++ b/apigen/ApiGen/ReflectionParameter.php @@ -0,0 +1,215 @@ +isArray()) { + return 'array'; + } elseif ($this->isCallable()) { + return 'callable'; + } elseif ($className = $this->getClassName()) { + return $className; + } elseif ($annotations = $this->getDeclaringFunction()->getAnnotation('param')) { + if (!empty($annotations[$this->getPosition()])) { + list($types) = preg_split('~\s+|$~', $annotations[$this->getPosition()], 2); + if (!empty($types) && '$' !== $types[0]) { + return $types; + } + } + } + + return 'mixed'; + } + + /** + * Returns the part of the source code defining the parameter default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->reflection->getDefaultValueDefinition(); + } + + /** + * Retutns if a default value for the parameter is available. + * + * @return boolean + */ + public function isDefaultValueAvailable() + { + return $this->reflection->isDefaultValueAvailable(); + } + + /** + * Returns the position within all parameters. + * + * @return integer + */ + public function getPosition() + { + return $this->reflection->position; + } + + /** + * Returns if the parameter expects an array. + * + * @return boolean + */ + public function isArray() + { + return $this->reflection->isArray(); + } + + /** + * Returns if the parameter expects a callback. + * + * @return boolean + */ + public function isCallable() + { + return $this->reflection->isCallable(); + } + + /** + * Returns reflection of the required class of the parameter. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getClass() + { + $className = $this->reflection->getClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the required class name of the value. + * + * @return string|null + */ + public function getClassName() + { + return $this->reflection->getClassName(); + } + + /** + * Returns if the the parameter allows NULL. + * + * @return boolean + */ + public function allowsNull() + { + return $this->reflection->allowsNull(); + } + + /** + * Returns if the parameter is optional. + * + * @return boolean + */ + public function isOptional() + { + return $this->reflection->isOptional(); + } + + /** + * Returns if the parameter value is passed by reference. + * + * @return boolean + */ + public function isPassedByReference() + { + return $this->reflection->isPassedByReference(); + } + + /** + * Returns if the paramter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return $this->reflection->canBePassedByValue(); + } + + /** + * Returns the declaring function. + * + * @return \ApiGen\ReflectionFunctionBase + */ + public function getDeclaringFunction() + { + $functionName = $this->reflection->getDeclaringFunctionName(); + + if ($className = $this->reflection->getDeclaringClassName()) { + return self::$parsedClasses[$className]->getMethod($functionName); + } else { + return self::$parsedFunctions[$functionName]; + } + } + + /** + * Returns the declaring function name. + * + * @return string + */ + public function getDeclaringFunctionName() + { + return $this->reflection->getDeclaringFunctionName(); + } + + /** + * Returns the function/method declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * If the parameter can be used unlimited. + * + * @return boolean + */ + public function isUnlimited() + { + return false; + } +} diff --git a/apigen/ApiGen/ReflectionParameterMagic.php b/apigen/ApiGen/ReflectionParameterMagic.php new file mode 100644 index 00000000000..287696b1547 --- /dev/null +++ b/apigen/ApiGen/ReflectionParameterMagic.php @@ -0,0 +1,479 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets parameter name. + * + * @param string $name + * @return \ApiGen\ReflectionParameterMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * Sets type hint. + * + * @param string $typeHint + * @return \ApiGen\ReflectionParameterMagic + */ + public function setTypeHint($typeHint) + { + $this->typeHint = (string) $typeHint; + return $this; + } + + /** + * Sets position of the parameter in the function/method. + * + * @param integer $position + * @return \ApiGen\ReflectionParameterMagic + */ + public function setPosition($position) + { + $this->position = (int) $position; + return $this; + } + + /** + * Sets the part of the source code defining the parameter default value. + * + * @param string|null $defaultValueDefinition + * @return \ApiGen\ReflectionParameterMagic + */ + public function setDefaultValueDefinition($defaultValueDefinition) + { + $this->defaultValueDefinition = $defaultValueDefinition; + return $this; + } + + /** + * Sets if the parameter can be used unlimited times. + * + * @param boolean $unlimited + * @return \ApiGen\ReflectionParameterMagic + */ + public function setUnlimited($unlimited) + { + $this->unlimited = (bool) $unlimited; + return $this; + } + + /** + * Sets if the parameter value is passed by reference. + * + * @param boolean $passedByReference + * @return \ApiGen\ReflectionParameterMagic + */ + public function setPassedByReference($passedByReference) + { + $this->passedByReference = (bool) $passedByReference; + return $this; + } + + /** + * Sets declaring function. + * + * @param \ApiGen\ReflectionFunctionBase $declaringFunction + * @return \ApiGen\ReflectionParameterMagic + */ + public function setDeclaringFunction(ReflectionFunctionBase $declaringFunction) + { + $this->declaringFunction = $declaringFunction; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringFunction->getBroker(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the type hint. + * + * @return string + */ + public function getTypeHint() + { + return $this->typeHint; + } + + /** + * Returns the file name the parameter is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringFunction->getFileName(); + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return str_replace('()', '($' . $this->name . ')', $this->declaringFunction->getPrettyName()); + } + + /** + * Returns the declaring class. + * + * @return \Apigen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringFunction->getDeclaringClass(); + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringFunction->getDeclaringClassName(); + } + + /** + * Returns the declaring function. + * + * @return \ApiGen\ReflectionFunctionBase + */ + public function getDeclaringFunction() + { + return $this->declaringFunction; + } + + /** + * Returns the declaring function name. + * + * @return string + */ + public function getDeclaringFunctionName() + { + return $this->declaringFunction->getName(); + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return $this->declaringFunction->getStartLine(); + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->declaringFunction->getEndLine(); + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Returns the part of the source code defining the parameter default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->defaultValueDefinition; + } + + /** + * Returns if a default value for the parameter is available. + * + * @return boolean + */ + public function isDefaultValueAvailable() + { + return null !== $this->defaultValueDefinition; + } + + /** + * Returns the position within all parameters. + * + * @return integer + */ + public function getPosition() + { + return $this->position; + } + + /** + * Returns if the parameter expects an array. + * + * @return boolean + */ + public function isArray() + { + return TokenReflection\ReflectionParameter::ARRAY_TYPE_HINT === $this->typeHint; + } + + public function isCallable() + { + return TokenReflection\ReflectionParameter::CALLABLE_TYPE_HINT === $this->typeHint; + } + + /** + * Returns reflection of the required class of the value. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getClass() + { + $className = $this->getClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the required class name of the value. + * + * @return string|null + */ + public function getClassName() + { + if ($this->isArray() || $this->isCallable()) { + return null; + } + + if (isset(self::$parsedClasses[$this->typeHint])) { + return $typeHint; + } + + return null; + } + + /** + * Returns if the the parameter allows NULL. + * + * @return boolean + */ + public function allowsNull() + { + if ($this->isArray() || $this->isCallable()) { + return 'null' === strtolower($this->defaultValueDefinition); + } + + return !empty($this->defaultValueDefinition); + } + + /** + * Returns if the parameter is optional. + * + * @return boolean + */ + public function isOptional() + { + return $this->isDefaultValueAvailable(); + } + + /** + * Returns if the parameter value is passed by reference. + * + * @return boolean + */ + public function isPassedByReference() + { + return $this->passedByReference; + } + + /** + * Returns if the parameter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return false; + } + + /** + * Returns if the parameter can be used unlimited times. + * + * @return boolean + */ + public function isUnlimited() + { + return $this->unlimited; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/apigen/ApiGen/ReflectionProperty.php b/apigen/ApiGen/ReflectionProperty.php new file mode 100644 index 00000000000..414cb1c388b --- /dev/null +++ b/apigen/ApiGen/ReflectionProperty.php @@ -0,0 +1,214 @@ +getAnnotation('var')) { + list($types) = preg_split('~\s+|$~', $annotations[0], 2); + if (!empty($types) && '$' !== $types[0]) { + return $types; + } + } + + try { + $type = gettype($this->getDefaultValue()); + if ('null' !== strtolower($type)) { + return $type; + } + } catch (\Exception $e) { + // Nothing + } + + return 'mixed'; + } + + /** + * Returns the property declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the name of the declaring class. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + return $this->reflection->getDefaultValue(); + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->reflection->getDefaultValueDefinition(); + } + + /** + * Returns if the property was created at compile time. + * + * @return boolean + */ + public function isDefault() + { + return $this->reflection->isDefault(); + } + + /** + * Returns property modifiers. + * + * @return integer + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the property is private. + * + * @return boolean + */ + public function isPrivate() + { + return $this->reflection->isPrivate(); + } + + /** + * Returns if the property is protected. + * + * @return boolean + */ + public function isProtected() + { + return $this->reflection->isProtected(); + } + + /** + * Returns if the property is public. + * + * @return boolean + */ + public function isPublic() + { + return $this->reflection->isPublic(); + } + + /** + * Returns if the poperty is static. + * + * @return boolean + */ + public function isStatic() + { + return $this->reflection->isStatic(); + } + + /** + * Returns the property declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + $traitName = $this->reflection->getDeclaringTraitName(); + return null === $traitName ? null : self::$parsedClasses[$traitName]; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->reflection->getDeclaringTraitName(); + } + + /** + * Returns if the property is valid. + * + * @return boolean + */ + public function isValid() + { + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } +} diff --git a/apigen/ApiGen/ReflectionPropertyMagic.php b/apigen/ApiGen/ReflectionPropertyMagic.php new file mode 100644 index 00000000000..98e7e2477ef --- /dev/null +++ b/apigen/ApiGen/ReflectionPropertyMagic.php @@ -0,0 +1,651 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets property name. + * + * @param string $name + * @return \Apigen\ReflectionPropertyMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + + } + + /** + * Sets type hint. + * + * @param string $typeHint + * @return \ApiGen\ReflectionParameterUnlimited + */ + public function setTypeHint($typeHint) + { + $this->typeHint = (string) $typeHint; + return $this; + } + + /** + * Sets short description. + * + * @param string $shortDescription + * @return \Apigen\ReflectionPropertyMagic + */ + public function setShortDescription($shortDescription) + { + $this->shortDescription = (string) $shortDescription; + return $this; + } + + /** + * Sets start line. + * + * @param integer $startLine + * @return \Apigen\ReflectionPropertyMagic + */ + public function setStartLine($startLine) + { + $this->startLine = (int) $startLine; + return $this; + } + + /** + * Sets end line. + * + * @param integer $endLine + * @return \Apigen\ReflectionPropertyMagic + */ + public function setEndLine($endLine) + { + $this->endLine = (int) $endLine; + return $this; + } + + /** + * Sets if the property is read-only. + * + * @param boolean $readOnly + * @return \Apigen\ReflectionPropertyMagic + */ + public function setReadOnly($readOnly) + { + $this->readOnly = (bool) $readOnly; + return $this; + } + + /** + * Sets if the property is write only. + * + * @param boolean $writeOnly + * @return \Apigen\ReflectionPropertyMagic + */ + public function setWriteOnly($writeOnly) + { + $this->writeOnly = (bool) $writeOnly; + return $this; + } + + /** + * Sets declaring class. + * + * @param \ApiGen\ReflectionClass $declaringClass + * @return \ApiGen\ReflectionPropertyMagic + */ + public function setDeclaringClass(ReflectionClass $declaringClass) + { + $this->declaringClass = $declaringClass; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringClass->getBroker(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->declaringClass->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->declaringClass->getEndPosition(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the type hint. + * + * @return string + */ + public function getTypeHint() + { + return $this->typeHint; + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + return $this->shortDescription; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + return $this->shortDescription; + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return $this->startLine; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->endLine; + } + + /** + * Returns if the property is read-only. + * + * @return boolean + */ + public function isReadOnly() + { + return $this->readOnly; + } + + /** + * Returns if the property is write-only. + * + * @return boolean + */ + public function isWriteOnly() + { + return $this->writeOnly; + } + + /** + * Returns if the property is magic. + * + * @return boolean + */ + public function isMagic() + { + return true; + } + + /** + * Returns the PHP extension reflection. + * + * @return \ApiGen\ReflectionExtension|null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns if the property should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = self::$config->deprecated || !$this->isDeprecated(); + } + + return $this->isDocumented; + } + + /** + * Returns if the property is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return $this->declaringClass->isDeprecated(); + } + + /** + * Returns property package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + return $this->declaringClass->getPackageName(); + } + + /** + * Returns property namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->declaringClass->getNamespaceName(); + } + + /** + * Returns property annotations. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + $this->annotations = array(); + } + return $this->annotations; + } + + /** + * Returns the property declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringClass; + } + + /** + * Returns the name of the declaring class. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->declaringClass->getName(); + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + return null; + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return ''; + } + + /** + * Returns if the property was created at compile time. + * + * @return boolean + */ + public function isDefault() + { + return false; + } + + /** + * Returns property modifiers. + * + * @return integer + */ + public function getModifiers() + { + return InternalReflectionProperty::IS_PUBLIC; + } + + /** + * Returns if the property is private. + * + * @return boolean + */ + public function isPrivate() + { + return false; + } + + /** + * Returns if the property is protected. + * + * @return boolean + */ + public function isProtected() + { + return false; + } + + /** + * Returns if the property is public. + * + * @return boolean + */ + public function isPublic() + { + return true; + } + + /** + * Returns if the poperty is static. + * + * @return boolean + */ + public function isStatic() + { + return false; + } + + /** + * Returns if the property is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns the property declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + return $this->declaringClass->isTrait() ? $this->declaringClass : null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + if ($declaringTrait = $this->getDeclaringTrait()) { + return $declaringTrait->getName(); + } + return null; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->declaringClass->getNamespaceAliases(); + } + + /** + * Returns an property pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::$%s', $this->declaringClass->getName(), $this->name); + } + + /** + * Returns the file name the property is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringClass->getFileName(); + } + + /** + * Returns if the property is user defined. + + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the property comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + $docComment = "/**\n"; + + if (!empty($this->shortDescription)) { + $docComment .= $this->shortDescription . "\n\n"; + } + + if ($annotations = $this->getAnnotation('var')) { + $docComment .= sprintf("@var %s\n", $annotations[0]); + } + + $docComment .= "*/\n"; + + return $docComment; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + $annotations = $this->getAnnotations(); + return array_key_exists($name, $annotations); + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + public function getAnnotation($name) + { + $annotations = $this->getAnnotations(); + if (array_key_exists($name, $annotations)) { + return $annotations[$name]; + } + return null; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/apigen/ApiGen/SourceFilesFilterIterator.php b/apigen/ApiGen/SourceFilesFilterIterator.php new file mode 100644 index 00000000000..dc841088a8b --- /dev/null +++ b/apigen/ApiGen/SourceFilesFilterIterator.php @@ -0,0 +1,75 @@ +excludeMasks = $excludeMasks; + } + + /** + * Returns if the current file/directory should be processed. + * + * @return boolean + */ + public function accept() { + /** @var \SplFileInfo */ + $current = $this->current(); + + foreach ($this->excludeMasks as $mask) { + if (fnmatch($mask, $current->getPathName(), FNM_NOESCAPE)) { + return false; + } + } + + if (!is_readable($current->getPathname())) { + throw new \InvalidArgumentException(sprintf('File/directory "%s" is not readable.', $current->getPathname())); + } + + return true; + } + + /** + * Returns the iterator of the current element's children. + * + * @return \ApiGen\SourceFilesFilterIterator + */ + public function getChildren() + { + return new static($this->getInnerIterator()->getChildren(), $this->excludeMasks); + } +} diff --git a/apigen/ApiGen/Template.php b/apigen/ApiGen/Template.php new file mode 100644 index 00000000000..ea97892e66c --- /dev/null +++ b/apigen/ApiGen/Template.php @@ -0,0 +1,809 @@ +generator = $generator; + $this->config = $generator->getConfig(); + + $that = $this; + + // Output in HTML5 + Nette\Utils\Html::$xhtml = false; + + // FSHL + $fshl = new FSHL\Highlighter(new FSHL\Output\Html()); + $fshl->setLexer(new FSHL\Lexer\Php()); + + // Texy + $this->texy = new \Texy(); + $this->texy->allowedTags = array_flip($this->config->allowedHtml); + $this->texy->allowed['list/definition'] = false; + $this->texy->allowed['phrase/em-alt'] = false; + $this->texy->allowed['longwords'] = false; + $this->texy->allowed['typography'] = false; + $this->texy->linkModule->shorten = false; + // Highlighting ,
+		$this->texy->addHandler('beforeParse', function($texy, &$text, $singleLine) {
+			$text = preg_replace('~(.+?)~', '#code#\\1#/code#', $text);
+		});
+		$this->texy->registerLinePattern(
+			function($parser, $matches, $name) use ($fshl) {
+				return \TexyHtml::el('code', $fshl->highlight($matches[1]));
+			},
+			'~#code#(.+?)#/code#~',
+			'codeInlineSyntax'
+		);
+		$this->texy->registerBlockPattern(
+			function($parser, $matches, $name) use ($fshl) {
+				if ('code' === $matches[1]) {
+					$lines = array_filter(explode("\n", $matches[2]));
+					if (!empty($lines)) {
+						$firstLine = array_shift($lines);
+
+						$indent = '';
+						$li = 0;
+
+						while (isset($firstLine[$li]) && preg_match('~\s~', $firstLine[$li])) {
+							foreach ($lines as $line) {
+								if (!isset($line[$li]) || $firstLine[$li] !== $line[$li]) {
+									break 2;
+								}
+							}
+
+							$indent .= $firstLine[$li++];
+						}
+
+						if (!empty($indent)) {
+							$matches[2] = str_replace(
+								"\n" . $indent,
+								"\n",
+								0 === strpos($matches[2], $indent) ? substr($matches[2], $li) : $matches[2]
+							);
+						}
+					}
+
+					$content = $fshl->highlight($matches[2]);
+				} else {
+					$content = htmlspecialchars($matches[2]);
+				}
+
+				$content = $parser->getTexy()->protect($content, \Texy::CONTENT_BLOCK);
+				return \TexyHtml::el('pre', $content);
+			},
+			'~<(code|pre)>(.+?)~s',
+			'codeBlockSyntax'
+		);
+
+		// Common operations
+		$this->registerHelperLoader('Nette\Templating\Helpers::loader');
+
+		// PHP source highlight
+		$this->registerHelper('highlightPHP', function($source, $context) use ($that, $fshl) {
+			return $that->resolveLink($that->getTypeName($source), $context) ?: $fshl->highlight((string) $source);
+		});
+		$this->registerHelper('highlightValue', function($definition, $context) use ($that) {
+			return $that->highlightPHP(preg_replace('~^(?:[ ]{4}|\t)~m', '', $definition), $context);
+		});
+
+		// Urls
+		$this->registerHelper('packageUrl', new Nette\Callback($this, 'getPackageUrl'));
+		$this->registerHelper('namespaceUrl', new Nette\Callback($this, 'getNamespaceUrl'));
+		$this->registerHelper('groupUrl', new Nette\Callback($this, 'getGroupUrl'));
+		$this->registerHelper('classUrl', new Nette\Callback($this, 'getClassUrl'));
+		$this->registerHelper('methodUrl', new Nette\Callback($this, 'getMethodUrl'));
+		$this->registerHelper('propertyUrl', new Nette\Callback($this, 'getPropertyUrl'));
+		$this->registerHelper('constantUrl', new Nette\Callback($this, 'getConstantUrl'));
+		$this->registerHelper('functionUrl', new Nette\Callback($this, 'getFunctionUrl'));
+		$this->registerHelper('elementUrl', new Nette\Callback($this, 'getElementUrl'));
+		$this->registerHelper('sourceUrl', new Nette\Callback($this, 'getSourceUrl'));
+		$this->registerHelper('manualUrl', new Nette\Callback($this, 'getManualUrl'));
+
+		// Packages & namespaces
+		$this->registerHelper('packageLinks', new Nette\Callback($this, 'getPackageLinks'));
+		$this->registerHelper('namespaceLinks', new Nette\Callback($this, 'getNamespaceLinks'));
+		$this->registerHelper('subgroupName', function($groupName) {
+			if ($pos = strrpos($groupName, '\\')) {
+				return substr($groupName, $pos + 1);
+			}
+			return $groupName;
+		});
+
+		// Types
+		$this->registerHelper('typeLinks', new Nette\Callback($this, 'getTypeLinks'));
+
+		// Docblock descriptions
+		$this->registerHelper('description', function($annotation, $context) use ($that) {
+			$description = trim(strpbrk($annotation, "\n\r\t $"));
+
+			if ($context instanceof ReflectionParameter) {
+				$description = preg_replace('~^(\\$' . $context->getName() . '(?:,\\.{3})?)(\s+|$)~i', '\\2', $description, 1);
+				$context = $context->getDeclaringFunction();
+			}
+			return $that->doc($description, $context);
+		});
+		$this->registerHelper('shortDescription', function($element, $block = false) use ($that) {
+			return $that->doc($element->getShortDescription(), $element, $block);
+		});
+		$this->registerHelper('longDescription', function($element) use ($that) {
+			$long = $element->getLongDescription();
+
+			// Merge lines
+			$long = preg_replace_callback('~(?:<(code|pre)>.+?)|([^<]*)~s', function($matches) {
+				return !empty($matches[2])
+					? preg_replace('~\n(?:\t|[ ])+~', ' ', $matches[2])
+					: $matches[0];
+			}, $long);
+
+			return $that->doc($long, $element, true);
+		});
+
+		// Individual annotations processing
+		$this->registerHelper('annotation', function($value, $name, $context) use ($that, $generator) {
+			switch ($name) {
+				case 'param':
+				case 'return':
+				case 'throws':
+					$description = $that->description($value, $context);
+					return sprintf('%s%s', $that->getTypeLinks($value, $context), $description ? '
' . $description : ''); + case 'license': + list($url, $description) = $that->split($value); + return $that->link($url, $description ?: $url); + case 'link': + list($url, $description) = $that->split($value); + if (Nette\Utils\Validators::isUrl($url)) { + return $that->link($url, $description ?: $url); + } + break; + case 'see': + $doc = array(); + foreach (preg_split('~\\s*,\\s*~', $value) as $link) { + if (null !== $generator->resolveElement($link, $context)) { + $doc[] = sprintf('%s', $that->getTypeLinks($link, $context)); + } else { + $doc[] = $that->doc($link, $context); + } + } + return implode(', ', $doc); + case 'uses': + case 'usedby': + list($link, $description) = $that->split($value); + $separator = $context instanceof ReflectionClass || !$description ? ' ' : '
'; + if (null !== $generator->resolveElement($link, $context)) { + return sprintf('%s%s%s', $that->getTypeLinks($link, $context), $separator, $description); + } + break; + default: + break; + } + + // Default + return $that->doc($value, $context); + }); + + $todo = $this->config->todo; + $internal = $this->config->internal; + $this->registerHelper('annotationFilter', function(array $annotations, array $filter = array()) use ($todo, $internal) { + // Filtered, unsupported or deprecated annotations + static $filtered = array( + 'package', 'subpackage', 'property', 'property-read', 'property-write', 'method', 'abstract', + 'access', 'final', 'filesource', 'global', 'name', 'static', 'staticvar' + ); + foreach ($filtered as $annotation) { + unset($annotations[$annotation]); + } + + // Custom filter + foreach ($filter as $annotation) { + unset($annotations[$annotation]); + } + + // Show/hide internal + if (!$internal) { + unset($annotations['internal']); + } + + // Show/hide tasks + if (!$todo) { + unset($annotations['todo']); + } + + return $annotations; + }); + + $this->registerHelper('annotationSort', function(array $annotations) { + uksort($annotations, function($one, $two) { + static $order = array( + 'deprecated' => 0, 'category' => 1, 'copyright' => 2, 'license' => 3, 'author' => 4, 'version' => 5, + 'since' => 6, 'see' => 7, 'uses' => 8, 'usedby' => 9, 'link' => 10, 'internal' => 11, + 'example' => 12, 'tutorial' => 13, 'todo' => 14 + ); + + if (isset($order[$one], $order[$two])) { + return $order[$one] - $order[$two]; + } elseif (isset($order[$one])) { + return -1; + } elseif (isset($order[$two])) { + return 1; + } else { + return strcasecmp($one, $two); + } + }); + return $annotations; + }); + + $this->registerHelper('annotationBeautify', function($annotation) { + static $names = array( + 'usedby' => 'Used by' + ); + + if (isset($names[$annotation])) { + return $names[$annotation]; + } + + return Nette\Utils\Strings::firstUpper($annotation); + }); + + // Static files versioning + $destination = $this->config->destination; + $this->registerHelper('staticFile', function($name) use ($destination) { + static $versions = array(); + + $filename = $destination . DIRECTORY_SEPARATOR . $name; + if (!isset($versions[$filename]) && is_file($filename)) { + $versions[$filename] = sprintf('%u', crc32(file_get_contents($filename))); + } + if (isset($versions[$filename])) { + $name .= '?' . $versions[$filename]; + } + return $name; + }); + + // Source anchors + $this->registerHelper('sourceAnchors', function($source) { + // Classes, interfaces, traits and exceptions + $source = preg_replace_callback('~((?:class|interface|trait)\\s+)(\\w+)~i', function($matches) { + $link = sprintf('%1$s', $matches[2]); + return $matches[1] . $link; + }, $source); + + // Methods and functions + $source = preg_replace_callback('~(function\\s+)(\\w+)~i', function($matches) { + $link = sprintf('%1$s', $matches[2]); + return $matches[1] . $link; + }, $source); + + // Constants + $source = preg_replace_callback('~(const)(.*?)(;)~is', function($matches) { + $links = preg_replace_callback('~(\\s|,)([A-Z_]+)(\\s+=)~', function($matches) { + return $matches[1] . sprintf('%1$s', $matches[2]) . $matches[3]; + }, $matches[2]); + return $matches[1] . $links . $matches[3]; + }, $source); + + // Properties + $source = preg_replace_callback('~((?:private|protected|public|var|static)\\s+)(.*?)(;)~is', function($matches) { + $links = preg_replace_callback('~()(\\$\\w+)~i', function($matches) { + return $matches[1] . sprintf('%1$s', $matches[2]); + }, $matches[2]); + return $matches[1] . $links . $matches[3]; + }, $source); + + return $source; + }); + + $this->registerHelper('urlize', array($this, 'urlize')); + + $this->registerHelper('relativePath', array($generator, 'getRelativePath')); + $this->registerHelper('resolveElement', array($generator, 'resolveElement')); + $this->registerHelper('getClass', array($generator, 'getClass')); + } + + /** + * Returns unified type value definition (class name or member data type). + * + * @param string $name + * @param boolean $trimNamespaceSeparator + * @return string + */ + public function getTypeName($name, $trimNamespaceSeparator = true) + { + static $names = array( + 'int' => 'integer', + 'bool' => 'boolean', + 'double' => 'float', + 'void' => '', + 'FALSE' => 'false', + 'TRUE' => 'true', + 'NULL' => 'null', + 'callback' => 'callable' + ); + + // Simple type + if (isset($names[$name])) { + return $names[$name]; + } + + // Class, constant or function + return $trimNamespaceSeparator ? ltrim($name, '\\') : $name; + } + + /** + * Returns links for types. + * + * @param string $annotation + * @param \ApiGen\ReflectionElement $context + * @return string + */ + public function getTypeLinks($annotation, ReflectionElement $context) + { + $links = array(); + + list($types) = $this->split($annotation); + if (!empty($types) && '$' === $types{0}) { + $types = null; + } + + if (empty($types)) { + $types = 'mixed'; + } + + foreach (explode('|', $types) as $type) { + $type = $this->getTypeName($type, false); + $links[] = $this->resolveLink($type, $context) ?: $this->escapeHtml(ltrim($type, '\\')); + } + + return implode('|', $links); + } + + /** + * Returns links for package/namespace and its parent packages. + * + * @param string $package + * @param boolean $last + * @return string + */ + public function getPackageLinks($package, $last = true) + { + if (empty($this->packages)) { + return $package; + } + + $links = array(); + + $parent = ''; + foreach (explode('\\', $package) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + $links[] = $last || $parent !== $package + ? $this->link($this->getPackageUrl($parent), $part) + : $this->escapeHtml($part); + } + + return implode('\\', $links); + } + + /** + * Returns links for namespace and its parent namespaces. + * + * @param string $namespace + * @param boolean $last + * @return string + */ + public function getNamespaceLinks($namespace, $last = true) + { + if (empty($this->namespaces)) { + return $namespace; + } + + $links = array(); + + $parent = ''; + foreach (explode('\\', $namespace) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + $links[] = $last || $parent !== $namespace + ? $this->link($this->getNamespaceUrl($parent), $part) + : $this->escapeHtml($part); + } + + return implode('\\', $links); + } + + /** + * Returns a link to a namespace summary file. + * + * @param string $namespaceName Namespace name + * @return string + */ + public function getNamespaceUrl($namespaceName) + { + return sprintf($this->config->template['templates']['main']['namespace']['filename'], $this->urlize($namespaceName)); + } + + /** + * Returns a link to a package summary file. + * + * @param string $packageName Package name + * @return string + */ + public function getPackageUrl($packageName) + { + return sprintf($this->config->template['templates']['main']['package']['filename'], $this->urlize($packageName)); + } + + /** + * Returns a link to a group summary file. + * + * @param string $groupName Group name + * @return string + */ + public function getGroupUrl($groupName) + { + if (!empty($this->packages)) { + return $this->getPackageUrl($groupName); + } + + return $this->getNamespaceUrl($groupName); + } + + /** + * Returns a link to class summary file. + * + * @param string|\ApiGen\ReflectionClass $class Class reflection or name + * @return string + */ + public function getClassUrl($class) + { + $className = $class instanceof ReflectionClass ? $class->getName() : $class; + return sprintf($this->config->template['templates']['main']['class']['filename'], $this->urlize($className)); + } + + /** + * Returns a link to method in class summary file. + * + * @param \ApiGen\ReflectionMethod $method Method reflection + * @param \ApiGen\ReflectionClass $class Method declaring class + * @return string + */ + public function getMethodUrl(ReflectionMethod $method, ReflectionClass $class = null) + { + $className = null !== $class ? $class->getName() : $method->getDeclaringClassName(); + return $this->getClassUrl($className) . '#' . ($method->isMagic() ? 'm' : '') . '_' . ($method->getOriginalName() ?: $method->getName()); + } + + /** + * Returns a link to property in class summary file. + * + * @param \ApiGen\ReflectionProperty $property Property reflection + * @param \ApiGen\ReflectionClass $class Property declaring class + * @return string + */ + public function getPropertyUrl(ReflectionProperty $property, ReflectionClass $class = null) + { + $className = null !== $class ? $class->getName() : $property->getDeclaringClassName(); + return $this->getClassUrl($className) . '#' . ($property->isMagic() ? 'm' : '') . '$' . $property->getName(); + } + + /** + * Returns a link to constant in class summary file or to constant summary file. + * + * @param \ApiGen\ReflectionConstant $constant Constant reflection + * @return string + */ + public function getConstantUrl(ReflectionConstant $constant) + { + // Class constant + if ($className = $constant->getDeclaringClassName()) { + return $this->getClassUrl($className) . '#' . $constant->getName(); + } + // Constant in namespace or global space + return sprintf($this->config->template['templates']['main']['constant']['filename'], $this->urlize($constant->getName())); + } + + /** + * Returns a link to function summary file. + * + * @param \ApiGen\ReflectionFunction $function Function reflection + * @return string + */ + public function getFunctionUrl(ReflectionFunction $function) + { + return sprintf($this->config->template['templates']['main']['function']['filename'], $this->urlize($function->getName())); + } + + /** + * Returns a link to element summary file. + * + * @param \ApiGen\ReflectionElement $element Element reflection + * @return string + */ + public function getElementUrl(ReflectionElement $element) + { + if ($element instanceof ReflectionClass) { + return $this->getClassUrl($element); + } elseif ($element instanceof ReflectionMethod) { + return $this->getMethodUrl($element); + } elseif ($element instanceof ReflectionProperty) { + return $this->getPropertyUrl($element); + } elseif ($element instanceof ReflectionConstant) { + return $this->getConstantUrl($element); + } elseif ($element instanceof ReflectionFunction) { + return $this->getFunctionUrl($element); + } + } + + /** + * Returns a link to a element source code. + * + * @param \ApiGen\ReflectionElement $element Element reflection + * @param boolean $withLine Include file line number into the link + * @return string + */ + public function getSourceUrl(ReflectionElement $element, $withLine = true) + { + if ($element instanceof ReflectionClass || $element instanceof ReflectionFunction || ($element instanceof ReflectionConstant && null === $element->getDeclaringClassName())) { + $elementName = $element->getName(); + + if ($element instanceof ReflectionClass) { + $file = 'class-'; + } elseif ($element instanceof ReflectionConstant) { + $file = 'constant-'; + } elseif ($element instanceof ReflectionFunction) { + $file = 'function-'; + } + } else { + $elementName = $element->getDeclaringClassName(); + $file = 'class-'; + } + + $file .= $this->urlize($elementName); + + $lines = null; + if ($withLine) { + $lines = $element->getStartLine() !== $element->getEndLine() ? sprintf('%s-%s', $element->getStartLine(), $element->getEndLine()) : $element->getStartLine(); + } + + return sprintf($this->config->template['templates']['main']['source']['filename'], $file) . (null !== $lines ? '#' . $lines : ''); + } + + /** + * Returns a link to a element documentation at php.net. + * + * @param \ApiGen\ReflectionBase $element Element reflection + * @return string + */ + public function getManualUrl(ReflectionBase $element) + { + static $manual = 'http://php.net/manual'; + static $reservedClasses = array('stdClass', 'Closure', 'Directory'); + + // Extension + if ($element instanceof ReflectionExtension) { + $extensionName = strtolower($element->getName()); + if ('core' === $extensionName) { + return $manual; + } + + if ('date' === $extensionName) { + $extensionName = 'datetime'; + } + + return sprintf('%s/book.%s.php', $manual, $extensionName); + } + + // Class and its members + $class = $element instanceof ReflectionClass ? $element : $element->getDeclaringClass(); + + if (in_array($class->getName(), $reservedClasses)) { + return $manual . '/reserved.classes.php'; + } + + $className = strtolower($class->getName()); + $classUrl = sprintf('%s/class.%s.php', $manual, $className); + $elementName = strtolower(strtr(ltrim($element->getName(), '_'), '_', '-')); + + if ($element instanceof ReflectionClass) { + return $classUrl; + } elseif ($element instanceof ReflectionMethod) { + return sprintf('%s/%s.%s.php', $manual, $className, $elementName); + } elseif ($element instanceof ReflectionProperty) { + return sprintf('%s#%s.props.%s', $classUrl, $className, $elementName); + } elseif ($element instanceof ReflectionConstant) { + return sprintf('%s#%s.constants.%s', $classUrl, $className, $elementName); + } + } + + /** + * Tries to parse a definition of a class/method/property/constant/function and returns the appropriate link if successful. + * + * @param string $definition Definition + * @param \ApiGen\ReflectionElement $context Link context + * @return string|null + */ + public function resolveLink($definition, ReflectionElement $context) + { + if (empty($definition)) { + return null; + } + + $suffix = ''; + if ('[]' === substr($definition, -2)) { + $definition = substr($definition, 0, -2); + $suffix = '[]'; + } + + $element = $this->generator->resolveElement($definition, $context, $expectedName); + if (null === $element) { + return $expectedName; + } + + $classes = array(); + if ($element->isDeprecated()) { + $classes[] = 'deprecated'; + } + if (!$element->isValid()) { + $classes[] = 'invalid'; + } + + if ($element instanceof ReflectionClass) { + $link = $this->link($this->getClassUrl($element), $element->getName(), true, $classes); + } elseif ($element instanceof ReflectionConstant && null === $element->getDeclaringClassName()) { + $text = $element->inNamespace() + ? $this->escapeHtml($element->getNamespaceName()) . '\\' . $this->escapeHtml($element->getShortName()) . '' + : '' . $this->escapeHtml($element->getName()) . ''; + $link = $this->link($this->getConstantUrl($element), $text, false, $classes); + } elseif ($element instanceof ReflectionFunction) { + $link = $this->link($this->getFunctionUrl($element), $element->getName() . '()', true, $classes); + } else { + $text = $this->escapeHtml($element->getDeclaringClassName()); + if ($element instanceof ReflectionProperty) { + $url = $this->propertyUrl($element); + $text .= '::$' . $this->escapeHtml($element->getName()) . ''; + } elseif ($element instanceof ReflectionMethod) { + $url = $this->methodUrl($element); + $text .= '::' . $this->escapeHtml($element->getName()) . '()'; + } elseif ($element instanceof ReflectionConstant) { + $url = $this->constantUrl($element); + $text .= '::' . $this->escapeHtml($element->getName()) . ''; + } + + $link = $this->link($url, $text, false, $classes); + } + + return sprintf('%s', $link . $suffix); + } + + /** + * Resolves links in documentation. + * + * @param string $text Processed documentation text + * @param \ApiGen\ReflectionElement $context Reflection object + * @return string + */ + private function resolveLinks($text, ReflectionElement $context) + { + $that = $this; + return preg_replace_callback('~{@(?:link|see)\\s+([^}]+)}~', function ($matches) use ($context, $that) { + // Texy already added so it has to be stripped + list($url, $description) = $that->split(strip_tags($matches[1])); + if (Nette\Utils\Validators::isUrl($url)) { + return $that->link($url, $description ?: $url); + } + return $that->resolveLink($matches[1], $context) ?: $matches[1]; + }, $text); + } + + /** + * Resolves internal annotation. + * + * @param string $text + * @return string + */ + private function resolveInternal($text) + { + $internal = $this->config->internal; + return preg_replace_callback('~\\{@(\\w+)(?:(?:\\s+((?>(?R)|[^{}]+)*)\\})|\\})~', function($matches) use ($internal) { + // Replace only internal + if ('internal' !== $matches[1]) { + return $matches[0]; + } + return $internal && isset($matches[2]) ? $matches[2] : ''; + }, $text); + } + + /** + * Formats text as documentation block or line. + * + * @param string $text Text + * @param \ApiGen\ReflectionElement $context Reflection object + * @param boolean $block Parse text as block + * @return string + */ + public function doc($text, ReflectionElement $context, $block = false) + { + return $this->resolveLinks($this->texy->process($this->resolveInternal($text), !$block), $context); + } + + /** + * Parses annotation value. + * + * @param string $value + * @return array + */ + public function split($value) + { + return preg_split('~\s+|$~', $value, 2); + } + + /** + * Returns link. + * + * @param string $url + * @param string $text + * @param boolean $escape If the text should be escaped + * @param array $classes List of classes + * @return string + */ + public function link($url, $text, $escape = true, array $classes = array()) + { + $class = !empty($classes) ? sprintf(' class="%s"', implode(' ', $classes)) : ''; + return sprintf('%s', $url, $class, $escape ? $this->escapeHtml($text) : $text); + } + + /** + * Converts string to url safe characters. + * + * @param string $string + * @return string + */ + public function urlize($string) + { + return preg_replace('~[^\w]~', '.', $string); + } +} diff --git a/apigen/ApiGen/Tree.php b/apigen/ApiGen/Tree.php new file mode 100644 index 00000000000..01f47b6b76b --- /dev/null +++ b/apigen/ApiGen/Tree.php @@ -0,0 +1,90 @@ +setPrefixPart(RecursiveTreeIterator::PREFIX_END_HAS_NEXT, self::HAS_NEXT); + $this->setPrefixPart(RecursiveTreeIterator::PREFIX_END_LAST, self::LAST); + $this->rewind(); + + $this->reflections = $reflections; + } + + /** + * Returns if the current item has a sibling on the same level. + * + * @return boolean + */ + public function hasSibling() + { + $prefix = $this->getPrefix(); + return !empty($prefix) && self::HAS_NEXT === substr($prefix, -1); + } + + /** + * Returns the current reflection. + * + * @return \ApiGen\Reflection + * @throws \UnexpectedValueException If current is not reflection array. + */ + public function current() + { + $className = $this->key(); + if (!isset($this->reflections[$className])) { + throw new RuntimeException(sprintf('Class "%s" is not in the reflection array', $className)); + } + + return $this->reflections[$className]; + } +} diff --git a/apigen/CHANGELOG.md b/apigen/CHANGELOG.md new file mode 100644 index 00000000000..a836e552830 --- /dev/null +++ b/apigen/CHANGELOG.md @@ -0,0 +1,128 @@ +## ApiGen 2.8.0 (2012-09-08) ## + +* Added support for @property and @method annotations +* Added support for variable length parameters +* Enabled selection of more rows in source code +* Templates can specify minimum and maximum required ApiGen version +* Added template for 404 page +* Improved support for malformed @param annotations +* Fixed excluding files and directories and detecting non accessible files and directories +* Fixed internal error when no timezone is specified in php.ini +* Fixed autocomplate in Opera browser +* Nette framework updated to version 2.0.5 +* TokenReflection library updated to version 1.3.1 +* FSHL library updated to version 2.1.0 + +## ApiGen 2.7.0 (2012-07-15) ## + +* Support of custom template macros and helpers +* Information about overridden methods in class method list +* Template UX fixes +* Fixed bugs causing ApiGen to crash +* TokenReflection library updated to version 1.3.0 +* Bootstrap2 based template +* Removed template with frames + +## ApiGen 2.6.1 (2012-03-27) ## + +* Fixed resolving element names in annotations +* Nette framework updated to version 2.0.1 +* TokenReflection library updated to version 1.2.2 + +## ApiGen 2.6.0 (2012-03-11) ## + +* Better error reporting, especially about duplicate classes, functions and constants +* Character set autodetection is on by default +* Changed visualization of deprecated elements +* Improved packages parsing and visualization +* Improved @license and @link visualization +* Improved `````` parsing +* Added option ```--extensions``` to specify file extensions of parsed files +* Minor visualization improvements +* Fixed autocomplete for classes in namespaces +* TokenReflection library updated to version 1.2.0 + +## ApiGen 2.5.0 (2012-02-12) ## + +* Added option ```--groups``` for grouping classes, interfaces, traits and exceptions in the menu +* Added option ```--autocomplete``` for choosing elements in the search autocomplete +* Inheriting some annotations from the file-level docblock +* @uses annotations create a @usedby annotation in the target documentation +* Added warning for unknown options +* Added support of comma-separated values for @see +* Changed all path options to be relative to the configuration file +* Fixed dependencies check +* Nette framework updated to 2.0.0 stable version +* TokenReflection library updated to version 1.1.0 + +## ApiGen 2.4.1 (2012-01-25) ## + +* TokenReflection library updated to version 1.0.2 +* Nette framework updated to version 2.0.0RC1 + +## ApiGen 2.4.0 (2011-12-24) ## + +* TokenReflection library updated to version 1.0.0 +* Fixed support for older PHP versions of the 5.3 branch +* Option ```templateConfig``` is relative to the config file (was relative to cwd) + +## ApiGen 2.3.0 (2011-11-13) ## + +* Added support for default configuration file +* Added link to download documentation as ZIP archive +* Added option ```--charset``` and autodetection of charsets +* Added support for @ignore annotation +* Added PHAR support +* Added support for ClassName[] +* Added memory usage reporting in progressbar +* Improved templates for small screens +* Changed option name ```--undocumented``` to ```--report``` +* FSHL library updated to version 2.0.1 + +## ApiGen 2.2.1 (2011-10-26) ## + +* Fixed processing of magic constants +* Fixed resize.png +* TokenReflection library updated to version 1.0.0RC2 + +## ApiGen 2.2.0 (2011-10-16) ## + +* Added an option to check for updates +* Added an option to initially display elements in alphabetical order +* Added an option to generate the robots.txt file +* Added required extensions check +* Changed reporting of undocumented elements to the checkstyle format +* Improved deprecated elements highlighting +* Highlighting the linked source code line +* Unknown annotations are sorted alphabetically +* Fixed class parameter description parsing +* Fixed command line options parsing +* Fixed include path setting of the GitHub version +* Fixed frames template + +## ApiGen 2.1.0 (2011-09-04) ## + +* Experimental support of PHP 5.4 traits +* Added option ```--colors``` +* Added template with frames +* Added templates option to make element details expanded by default + +## ApiGen 2.0.3 (2011-08-22) ## + +* @param, @return and @throw annotations are inherited + +## ApiGen 2.0.2 (2011-07-21) ## + +* Fixed inherited methods listing +* Interfaces are not labeled "Abstract interface" +* Fixed Google CSE ID validation +* Fixed filtering by ```--exclude``` and ```--skip-doc-path``` +* Fixed exception output when using ```--debug``` + +## ApiGen 2.0.1 (2011-07-17) ## + +* Updated TokenReflection library to 1.0.0beta5 +* Requires FSHL 2.0.0RC +* Fixed url in footer + +## ApiGen 2.0.0 (2011-06-28) ## diff --git a/apigen/LICENSE.md b/apigen/LICENSE.md new file mode 100644 index 00000000000..afe8631e4e6 --- /dev/null +++ b/apigen/LICENSE.md @@ -0,0 +1,32 @@ +# Licenses # + +You may use ApiGen under the terms of either the New BSD License or the GNU General Public License (GPL) version 2 or 3. + +The BSD License is recommended for most projects. It is easy to understand and it places almost no restrictions on what you can do with the framework. If the GPL fits better to your project, you can use the framework under this license. + +You don't have to notify anyone which license you are using. You can freely use ApiGen in commercial projects as long as the copyright header remains intact. + +## New BSD License ## + +Copyright (c) 2010 [David Grudl](http://davidgrudl.com) +Copyright (c) 2011-2012 [Jaroslav Hanslík](https://github.com/kukulich) +Copyright (c) 2011-2012 [Ondřej Nešpor](https://github.com/Andrewsville) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +* Neither the name of "ApiGen" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +## GNU General Public License ## + +GPL licenses are very very long, so instead of including them here we offer you URLs with full text: + +* [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) +* [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) diff --git a/apigen/README.md b/apigen/README.md new file mode 100644 index 00000000000..14634f8959b --- /dev/null +++ b/apigen/README.md @@ -0,0 +1,285 @@ +# Welcome to ApiGen # + +ApiGen is the tool for creating professional API documentation from PHP source code, similar to discontinued phpDocumentor/phpDoc. + +ApiGen has support for PHP 5.3 namespaces, packages, linking between documentation, cross referencing to PHP standard classes and general documentation, creation of highlighted source code and experimental support for PHP 5.4 **traits**. + +## Support & Bug Reports ## + +For all support questions please use our [mailing list](https://groups.google.com/group/apigen). For bug reports and issues the [issue tracker](https://github.com/apigen/apigen/issues) is available. Changes between versions are described in the [change log](https://github.com/apigen/apigen/blob/master/CHANGELOG.md). + +## Features ## + +* Our own [TokenReflection library](https://github.com/Andrewsville/PHP-Token-Reflection) is used to describe the source code. It is **safe** (documented source code does not get included and thus parsed) and **simple** (you do not need to include or autoload all libraries you use in your source code). +* Detailed documentation of classes, functions and constants. +* Highlighted source code. +* Support of namespaces and packages with subpackages. +* Experimental support of traits. +* A page with trees of classes, interfaces, traits and exceptions. +* A page with a list of deprecated elements. +* A page with Todo tasks. +* Link to download documentation as ZIP archive. +* Checkstyle report of poorly documented elements. +* Support for docblock templates. +* Support for @inheritdoc. +* Support for {@link}. +* Active links in @see and @uses tags. +* Documentation of used internal PHP classes. +* Links to the start line in the highlighted source code for every described element. +* List of direct and indirect known subclasses, implementers and users for every class/interface/trait/exception. +* Check for a new version. +* Google CSE support with suggest. +* Google Analytics support. +* Support for multiple/custom templates. +* Sitemap and opensearch support. +* Support for different charsets and line endings. +* Lots of configuration options (see below). + +## Installation ## + +The preferred installation way is using the PEAR package but there are three more ways how to install ApiGen. + + +### PEAR ### + +PEAR is a distribution system for PHP packages. It is bundled with PHP since the 4.3 version and it is easy to use. + +The PEAR package contains only ApiGen itself. Its dependencies (Nette, Texy, FSHL and TokenReflection) have to be installed separately. But do not panic, the PEAR installer can take care of it. + +The easiest way is to use the PEAR auto discovery feature. In that case all you have to do is to type two commands. + +``` + pear config-set auto_discover 1 + pear install pear.apigen.org/apigen +``` + +If you don't want to use the auto discovery, you have to add PEAR channels of all ApiGen libraries manually. In this case you can install ApiGen by typing these commands. + +``` + pear channel-discover pear.apigen.org + pear channel-discover pear.nette.org + pear channel-discover pear.texy.info + pear channel-discover pear.kukulich.cz + pear channel-discover pear.andrewsville.cz + + pear install apigen/ApiGen +``` + +If you encounter a message like `WARNING: channel "pear.apigen.org" has updated its protocols, use "pear channel-update pear.apigen.org" to update`, you need to tell PEAR to update its information about the ApiGen channel using the suggested command. + +``` +pear channel-update pear.apigen.org +``` + +### Standalone package ### + +Using the standalone package is even easier than using the PEAR installer but it does not handle updates automatically. + +To download the actual release visit the [Downloads section](https://github.com/apigen/apigen/downloads). There you find separate packages for each release in two formats - zip and tar.gz. These packages are prepared by the ApiGen team and are truly standalone; they contain all required libraries in appropriate versions. You just need to extract the contents of an archive and you can start using ApiGen. + +### GitHub built archive ### + +GitHub allows you to download any repository as a zip or tar.gz archive. You can use this feature to download an archive with the current version of ApiGen. However this approach has one disadvantage. Such archive (in contrast to the standalone packages) does not contain required libraries. They are included as git submodules in the repository and GitHub simply ignores them when generating the archive. It means that you will have to obtain required libraries manually. + +### Cloning the repository ### + +The last way how to install ApiGen is simply to clone our repository. If you do so, remember to fetch and rebase to get new versions and do not forget to update submodules in the libs directory. + +## Usage ## + +``` + apigen --config [options] + apigen --source --destination [options] +``` + +As you can see, you can use ApiGen either by providing individual parameters via the command line or using a config file. Moreover you can combine the two methods and the command line parameters will have precedence over those in the config file. + +Every configuration option has to be followed by its value. And it is exactly the same to write ```--config=file.conf``` and ```--config file.conf```. The only exceptions are boolean options (those with yes|no values). When using these options on the command line you do not have to provide the "yes" value explicitly. If ommited, it is assumed that you wanted to turn the option on. So using ```--debug=yes``` and ```--debug``` does exactly the same (and the opposite is ```--debug=no```). + +Some options can have multiple values. To do so, you can either use them multiple times or separate their values by a comma. It means that ```--source=file1.php --source=file2.php``` and ```--source=file1.php,file2.php``` is exactly the same. + +### Options ### + +```--config|-c ``` + +Path to the config file. + +```--source|-s ``` **required** + +Path to the directory or file to be processed. You can use the parameter multiple times to provide a list of directories or files. All types of PHAR archives are supported (requires the PHAR extension). To process gz/bz2 compressed archives you need the appropriate extension (see requirements). + +```--destination|-d ``` **required** + +Documentation will be generated into this directory. + +```--extensions ``` + +List of allowed file extensions, default is "php". + +```--exclude ``` + +Directories and files matching this file mask will not be parsed. You can exclude for example tests from processing this way. This parameter is case sensitive and can be used multiple times. + +```--skip-doc-path ``` +```--skip-doc-prefix ``` + +Using this parameters you can tell ApiGen not to generate documentation for elements from certain files or with certain name prefix. Such classes will appear in class trees, but will not create a link to their documentation. These parameters are case sensitive and can be used multiple times. + +```--charset ``` + +Character set of source files, default is "auto" that lets ApiGen choose from all supported character sets. However if you use only one characters set across your source files you should set it explicitly to avoid autodetection because it can be tricky (and is not completely realiable). Moreover autodetection slows down the process of generating documentation. You can also use the parameter multiple times to provide a list of all used character sets in your documentation. In that case ApiGen will choose one of provided character sets for each file. + +```--main ``` + +Elements with this name prefix will be considered as the "main project" (the rest will be considered as libraries). + +```--title ``` + +Title of the generated documentation. + +```--base-url ``` + +Documentation base URL used in the sitemap. Only needed if you plan to make your documentation public. + +```--google-cse-id ``` + +If you have a Google CSE ID, the search box will use it when you do not enter an exact class, constant or function name. + +```--google-cse-label ``` + +This will be the default label when using Google CSE. + +```--google-analytics ``` + +A Google Analytics tracking code. If provided, an ansynchronous tracking code will be placed into every generated page. + +```--template-config ``` + +Template config file, default is the config file of ApiGen default template. + +```--allowed-html ``` + +List of allowed HTML tags in documentation separated by comma. Default value is "b,i,a,ul,ol,li,p,br,var,samp,kbd,tt". + +```--groups ``` + +How should elements be grouped in the menu. Possible options are "auto", "namespaces", "packages" and "none". Default value is "auto" (namespaces are used if the source code uses them, packages otherwise). + +```--autocomplete ``` + +List of element types that will appear in the search input autocomplete. Possible values are "classes", "constants", "functions", "methods", "properties" and "classconstants". Default value is "classes,constants,functions". + +```--access-levels ``` + +Access levels of methods and properties that should get their documentation parsed. Default value is "public,protected" (don't generate private class members). + +```--internal ``` + +Generate documentation for elements marked as internal (```@internal``` without description) and display parts of the documentation that are marked as internal (```@internal with description ...``` or inline ```{@internal ...}```), default is "No". + +```--php ``` + +Generate documentation for PHP internal classes, default is "Yes". + +```--tree ``` + +Generate tree view of classes, interfaces, traits and exceptions, default is "Yes". + +```--deprecated ``` + +Generate documentation for deprecated elements, default is "No". + +```--todo ``` + +Generate a list of tasks, default is "No". + +```--source-code ``` + +Generate highlighted source code for user defined elements, default is "Yes". + +```--download ``` + +Add a link to download documentation as a ZIP archive, default is "No". + +```--report ``` + +Save a checkstyle report of poorly documented elements into a file. + +```--wipeout ``` + +Delete files generated in the previous run, default is "Yes". + +```--quiet ``` + +Do not print any messages to the console, default is "No". + +```--progressbar ``` + +Display progressbars, default is "Yes". + +```--colors ``` + +Use colors, default "No" on Windows, "Yes" on other systems. Windows doesn't support colors in console however you can enable it with [Ansicon](http://adoxa.110mb.com/ansicon/). + +```--update-check ``` + +Check for a new version of ApiGen, default is "Yes". + +```--debug ``` + +Display additional information (exception trace) in case of an error, default is "No". + +```--help|-h ``` + +Display the list of possible options. + +Only ```--source``` and ```--destination``` parameters are required. You can provide them via command line or a configuration file. + +### Config files ### + +Instead of providing individual parameters via the command line, you can prepare a config file for later use. You can use all the above listed parameters (with one exception: the ```--config``` option) only without dashes and with an uppercase letter after each dash (so ```--access-level``` becomes ```accessLevel```). + +ApiGen uses the [NEON file format](http://ne-on.org) for all its config files. You can try the [online parser](http://ne-on.org) to debug your config files and see how they get parsed. + +Then you can call ApiGen with a single parameter ```--config``` specifying the config file to load. + +``` + apigen --config [options] +``` + +Even when using a config file, you can still provide additional parameters via the command line. Such parameters will have precedence over parameters from the config file. + +Keep in mind, that any values in the config file will be **overwritten** by values from the command line. That means that providing the ```--source``` parameter values both in the config file and via the command line will not result in using all the provided values but only those from the command line. + +If you provide no command line parameters at all, ApiGen will try to load a default config file called ```apigen.neon``` in the current working directory. If found it will work as if you used the ```--config``` option. Note that when using any command line option, you have to specify the config file if you have one. ApiGen will try to load one automatically only when no command line parameters are used. Option names have to be in camelCase in config files (```--template-config``` on the command line becomes ```templateConfig``` in a config file). You can see a full list of configuration options with short descriptions in the example config file [apigen.neon.example](https://github.com/apigen/apigen/blob/master/apigen.neon.example). + +### Example ### + +We are generating documentation for the Nella Framework. We want Nette and Doctrine to be parsed as well because we want their classes to appear in class trees, lists of parent classes and their members in lists of inherited properties, methods and constants. However we do not want to generate their full documentation along with highlighted source codes. And we do not want to process any "test" directories, because there might be classes that do not belong to the project actually. + +``` + apigen --source ~/nella/Nella --source ~/doctrine2/lib/Doctrine --source ~/doctrine2/lib/vendor --source ~/nette/Nette --skip-doc-path "~/doctrine2/*" --skip-doc-prefix Nette --exclude "*/tests/*" --destination ~/docs/ --title "Nella Framework" +``` + +## Requirements ## + +ApiGen requires PHP 5.3 or later. Four libraries it uses ([Nette](https://github.com/nette/nette), [Texy](https://github.com/dg/texy), [TokenReflection](https://github.com/Andrewsville/PHP-Token-Reflection) and [FSHL](https://github.com/kukulich/fshl)) require four additional PHP extensions: [tokenizer](http://php.net/manual/book.tokenizer.php), [mbstring](http://php.net/manual/book.mbstring.php), [iconv](http://php.net/manual/book.iconv.php) and [json](http://php.net/manual/book.json.php). For documenting PHAR archives you need the [phar extension](http://php.net/manual/book.phar.php) and for documenting gz or bz2 compressed PHARs, you need the [zlib](http://php.net/manual/book.zlib.php) or [bz2](http://php.net/manual/book.bzip2.php) extension respectively. To generate the ZIP file with documentation you need the [zip extension](http://php.net/manual/book.zip.php). + +When generating documentation of large libraries (Zend Framework for example) we recommend not to have the Xdebug PHP extension loaded (it does not need to be used, it significantly slows down the generating process even when only loaded). + +## Authors ## + +* [Jaroslav Hanslík](https://github.com/kukulich) +* [Ondřej Nešpor](https://github.com/Andrewsville) +* [David Grudl](https://github.com/dg) + +## Usage examples ## + +* [Doctrine](http://www.doctrine-project.org/api/orm/2.2/index.html) +* [Nette Framework](http://api.nette.org/2.0/) +* [TokenReflection library](http://andrewsville.github.com/PHP-Token-Reflection/) +* [FSHL library](http://fshl.kukulich.cz/api/) +* [Nella Framework](http://api.nellafw.org/) +* Jyxo PHP Libraries, both [namespaced](http://jyxo.github.com/php/) and [non-namespaced](http://jyxo.github.com/php-no-namespace/) + +Besides from these publicly visible examples there are companies that use ApiGen to generate their inhouse documentation: [Medio Interactive](http://www.medio.cz/), [Wikidi](http://wikidi.com/). \ No newline at end of file diff --git a/apigen/apigen.bat b/apigen/apigen.bat new file mode 100755 index 00000000000..9be5228e5dc --- /dev/null +++ b/apigen/apigen.bat @@ -0,0 +1,16 @@ +@echo off +REM ApiGen 2.8.0 - API documentation generator for PHP 5.3+ +REM +REM Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +REM Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +REM Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +REM +REM For the full copyright and license information, please view +REM the file LICENCE.md that was distributed with this source code. +REM + +IF EXIST "@php_bin@" ( + "@php_bin@" "@bin_dir@\apigen" %* +) ELSE ( + "php.exe" "%~dp0apigen.php" %* +) diff --git a/apigen/apigen.neon.example b/apigen/apigen.neon.example new file mode 100644 index 00000000000..e5e0de7f2f4 --- /dev/null +++ b/apigen/apigen.neon.example @@ -0,0 +1,67 @@ +# Source file or directory to parse +source: +# Directory where to save the generated documentation +destination: +# List of allowed file extensions +extensions: [php] +# Mask to exclude file or directory from processing +exclude: +# Don't generate documentation for classes from file or directory with this mask +skipDocPath: +# Don't generate documentation for classes with this name prefix +skipDocPrefix: +# Character set of source files +charset: auto +# Main project name prefix +main: + +# Title of generated documentation +title: +# Documentation base URL +baseUrl: +# Google Custom Search ID +googleCseId: +# Google Custom Search label +googleCseLabel: +# Google Analytics tracking code +googleAnalytics: +# Template config file +templateConfig: './templates/default/config.neon' +# Grouping of classes +groups: auto +# List of allowed HTML tags in documentation +allowedHtml: [b, i, a, ul, ol, li, p, br, var, samp, kbd, tt] +# Element types for search input autocomplete +autocomplete: [classes, constants, functions] + +# Generate documentation for methods and properties with given access level +accessLevels: [public, protected] +# Generate documentation for elements marked as internal and display internal documentation parts +internal: No +# Generate documentation for PHP internal classes +php: Yes +# Generate tree view of classes, interfaces and exceptions +tree: Yes +# Generate documentation for deprecated classes, methods, properties and constants +deprecated: No +# Generate documentation of tasks +todo: No +# Generate highlighted source code files +sourceCode: Yes +# Add a link to download documentation as a ZIP archive +download: No +# Save a checkstyle report of poorly documented elements into a file +report: + +# Wipe out the destination directory first +wipeout: Yes +# Don't display scanning and generating messages +quiet: No +# Display progressbars +progressbar: Yes +# Use colors +colors: No +# Check for update +updateCheck: Yes +# Display additional information in case of an error +debug: No diff --git a/apigen/apigen.php b/apigen/apigen.php new file mode 100644 index 00000000000..2b4aacd0310 --- /dev/null +++ b/apigen/apigen.php @@ -0,0 +1,254 @@ +#!/usr/bin/env php +processCliOptions($options); + $generator = new Generator($config); + + // Help + if ($config->isHelpRequested()) { + echo $generator->colorize($generator->getHeader()); + echo $generator->colorize($config->getHelp()); + die(); + } + + // Prepare configuration + $config->prepare(); + + if ($config->debug) { + Debugger::$onFatalError = array(); + Debugger::enable(Debugger::DEVELOPMENT, false); + } + + $generator->output($generator->getHeader()); + + // Check for update (only in production mode) + if ($config->updateCheck && !$config->debug) { + ini_set('default_socket_timeout', 5); + $latestVersion = @file_get_contents('http://pear.apigen.org/rest/r/apigen/latest.txt'); + if (false !== $latestVersion && version_compare(trim($latestVersion), Generator::VERSION, '>')) { + $generator->output(sprintf("New version @header@%s@c available\n\n", $latestVersion)); + } + } + + // Scan + if (count($config->source) > 1) { + $generator->output(sprintf("Scanning\n @value@%s@c\n", implode("\n ", $config->source))); + } else { + $generator->output(sprintf("Scanning @value@%s@c\n", $config->source[0])); + } + if (count($config->exclude) > 1) { + $generator->output(sprintf("Excluding\n @value@%s@c\n", implode("\n ", $config->exclude))); + } elseif (!empty($config->exclude)) { + $generator->output(sprintf("Excluding @value@%s@c\n", $config->exclude[0])); + } + + $parsed = $generator->parse(); + + if (count($parsed->errors) > 1) { + $generator->output(sprintf("@error@Found %d errors@c\n\n", count($parsed->errors))); + + $no = 1; + foreach ($parsed->errors as $e) { + + if ($e instanceof TokenReflection\Exception\ParseException) { + $generator->output(sprintf("@error@%d.@c The TokenReflection library threw an exception while parsing the file @value@%s@c.\n", $no, $e->getFileName())); + if ($config->debug) { + $generator->output("\nThis can have two reasons: a) the source code in the file is not valid or b) you have just found a bug in the TokenReflection library.\n\n"); + $generator->output("If the license allows it please send the whole file or at least the following fragment describing where exacly is the problem along with the backtrace to apigen@apigen.org. Thank you!\n\n"); + + $token = $e->getToken(); + $sender = $e->getSender(); + if (!empty($token)) { + $generator->output( + sprintf( + "The cause of the exception \"%s\" was the @value@%s@c token (line @count@%d@c) in following part of %s source code:\n\n", + $e->getMessage(), + $e->getTokenName(), + $e->getExceptionLine(), + $sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the' + ) + ); + } else { + $generator->output( + sprintf( + "The exception \"%s\" was thrown when processing %s source code:\n\n", + $e->getMessage(), + $sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the' + ) + ); + } + + $generator->output($e->getSourcePart(true) . "\n\nThe exception backtrace is following:\n\n" . $e->getTraceAsString() . "\n\n"); + } + } elseif ($e instanceof TokenReflection\Exception\FileProcessingException) { + $generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage())); + if ($config->debug) { + $generator->output("\n" . $e->getDetail() . "\n\n"); + } + } else { + $generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage())); + if ($config->debug) { + $trace = $e->getTraceAsString(); + while ($e = $e->getPrevious()) { + $generator->output(sprintf("\n%s", $e->getMessage())); + $trace = $e->getTraceAsString(); + } + $generator->output(sprintf("\n%s\n\n", $trace)); + } + } + + $no++; + } + + if (!$config->debug) { + $generator->output("\nEnable the debug mode (@option@--debug@c) to see more details.\n\n"); + } + } + + $generator->output(sprintf("Found @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes\n", $parsed->classes, $parsed->constants, $parsed->functions, $parsed->internalClasses)); + $generator->output(sprintf("Documentation for @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes will be generated\n", $parsed->documentedClasses, $parsed->documentedConstants, $parsed->documentedFunctions, $parsed->documentedInternalClasses)); + + // Generating + $generator->output(sprintf("Using template config file @value@%s@c\n", $config->templateConfig)); + + if ($config->wipeout && is_dir($config->destination)) { + $generator->output("Wiping out destination directory\n"); + if (!$generator->wipeOutDestination()) { + throw new \RuntimeException('Cannot wipe out destination directory'); + } + } + + $generator->output(sprintf("Generating to directory @value@%s@c\n", $config->destination)); + $skipping = array_merge($config->skipDocPath, $config->skipDocPrefix); + if (count($skipping) > 1) { + $generator->output(sprintf("Will not generate documentation for\n @value@%s@c\n", implode("\n ", $skipping))); + } elseif (!empty($skipping)) { + $generator->output(sprintf("Will not generate documentation for @value@%s@c\n", $skipping[0])); + } + $generator->generate(); + + // End + $end = new \DateTime(); + $interval = $end->diff($start); + $parts = array(); + if ($interval->h > 0) { + $parts[] = sprintf('@count@%d@c hours', $interval->h); + } + if ($interval->i > 0) { + $parts[] = sprintf('@count@%d@c min', $interval->i); + } + if ($interval->s > 0) { + $parts[] = sprintf('@count@%d@c sec', $interval->s); + } + if (empty($parts)) { + $parts[] = sprintf('@count@%d@c sec', 1); + } + + $duration = implode(' ', $parts); + $generator->output(sprintf("Done. Total time: %s, used: @count@%d@c MB RAM\n", $duration, round(memory_get_peak_usage(true) / 1024 / 1024))); + +} catch (ConfigException $e) { + // Configuration error + echo $generator->colorize($generator->getHeader() . sprintf("\n@error@%s@c\n\n", $e->getMessage()) . $config->getHelp()); + + die(2); +} catch (\Exception $e) { + // Everything else + if ($config->debug) { + do { + echo $generator->colorize(sprintf("\n%s(%d): @error@%s@c", $e->getFile(), $e->getLine(), $e->getMessage())); + $trace = $e->getTraceAsString(); + } while ($e = $e->getPrevious()); + + printf("\n\n%s\n", $trace); + } else { + echo $generator->colorize(sprintf("\n@error@%s@c\n", $e->getMessage())); + } + + die(1); +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Generator.php b/apigen/libs/FSHL/FSHL/Generator.php new file mode 100644 index 00000000000..943a13857b2 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Generator.php @@ -0,0 +1,624 @@ +lexer = $lexer; + $this->lexerName = $lexer->getLanguage(); + $this->source = $this->generate(); + } + + /** + * Returns the generated source. + * + * @return string + */ + public function getSource() + { + return $this->source; + } + + /** + * Saves the generated source to a lexer cache file. + * + * @return \FSHL\Generator + * @throws \RuntimeException If the file could not be saved. + */ + public function saveToCache() + { + $file = __DIR__ . '/Lexer/Cache/' . $this->lexerName . '.php'; + if (false === @file_put_contents($file, $this->getSource())) { + throw new \RuntimeException(sprintf('Cannot save source to "%s"', $file)); + } + require_once $file; + return $this; + } + + /** + * Generates the source. + * + * @return string + */ + private function generate() + { + $this->optimize(); + + $constructor = ''; + $constructor .= $this->getVarSource('$this->language', $this->lexer->getLanguage()); + $constructor .= $this->getVarSource('$this->trans', $this->trans); + $constructor .= $this->getVarSource('$this->initialState', $this->states[$this->lexer->getInitialState()]); + $constructor .= $this->getVarSource('$this->returnState', $this->states[self::STATE_RETURN]); + $constructor .= $this->getVarSource('$this->quitState', $this->states[self::STATE_QUIT]); + $constructor .= $this->getVarSource('$this->flags', $this->flags); + $constructor .= $this->getVarSource('$this->data', $this->data); + $constructor .= $this->getVarSource('$this->classes', $this->classes); + $constructor .= $this->getVarSource('$this->keywords', $this->lexer->getKeywords()); + + $functions = ''; + foreach ($this->delimiters as $state => $delimiter) { + if (null !== $delimiter) { + $functions .= $this->generateState($state); + } + } + + return <<lexerName} lexer. + * + * This file is generated. All changes made in this file will be lost. + * + * @copyright Copyright (c) 2002-2005 Juraj 'hvge' Durech + * @copyright Copyright (c) 2011-2012 Jaroslav Hanslík + * @license http://fshl.kukulich.cz/#license + * @see \\FSHL\\Generator + * @see \\FSHL\\Lexer\\{$this->lexerName} + */ +class {$this->lexerName} +{ + /** + * Language name. + * + * @var array + */ + public \$language; + + /** + * Transitions table. + * + * @var array + */ + public \$trans; + + /** + * Id of the initial state. + * + * @var integer + */ + public \$initialState; + + /** + * Id of the return state. + * + * @var integer + */ + public \$returnState; + + /** + * Id of the quit state. + * + * @var integer + */ + public \$quitState; + + /** + * List of flags for all states. + * + * @var array + */ + public \$flags; + + /** + * Data for all states. + * + * @var array + */ + public \$data; + + /** + * List of CSS classes. + * + * @var array + */ + public \$classes; + + /** + * List of keywords. + * + * @var array + */ + public \$keywords; + + /** + * Initializes the lexer. + */ + public function __construct() + { +$constructor + } +$functions +} +SOURCE; + } + + /** + * Generates a state code. + * + * @param integer $state + * @return string + */ + private function generateState($state) + { + // Delimiter => Condition + static $commonDelimiters = array( + 'ALL' => true, + 'LINE' => "\n", + 'TAB' => "\t", + 'SPACE' => 'preg_match(\'~^\\s+~\', $part, $matches)', + '!SPACE' => 'preg_match(\'~^\\\\S+~\', $part, $matches)', + 'ALPHA' => 'preg_match(\'~^[a-z]+~i\', $part, $matches)', + '!ALPHA' => 'preg_match(\'~^[^a-z]+~i\', $part, $matches)', + 'ALNUM' => 'preg_match(\'~^[a-z\\\\d]+~i\', $part, $matches)', + '!ALNUM' => 'preg_match(\'~^[^a-z\\\\d]+~i\', $part, $matches)', + 'ALNUM_' => 'preg_match(\'~^\\\\w+~\', $part, $matches)', + '!ALNUM_' => 'preg_match(\'~^\\\\W+~\', $part, $matches)', + 'NUM' => 'preg_match(\'~^\\\\d+~\', $part, $matches)', + '!NUM' => 'preg_match(\'~^\\\\D+~\', $part, $matches)', + 'HEXNUM' => 'preg_match(\'~^[a-f\\\\d]+~i\', $part, $matches)', + '!HEXNUM' => 'preg_match(\'~^[^a-f\\\\d]+~i\', $part, $matches)', + 'DOTNUM' => 'preg_match(\'~^\\.\\\\d+~\', $part, $matches)', + '!DOTNUM' => 'preg_match(\'~^(?:[^\\.]|\\.\\\\D)~\', $part, $matches)' + ); + + $allDelimiters = array_merge($commonDelimiters, $this->lexer->getDelimiters()); + + $conditionsSource = ''; + $delimiters = array(); + foreach ($this->delimiters[$state] as $no => $delimiter) { + if ('ALL' === $delimiter) { + $conditionSource = <<states) . '. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter' . $state . '($text, $textLength, $textPos) + { + ' . (!empty($delimiters) ? sprintf('static $delimiters = %s;', $this->getVarValueSource($delimiters)) : '') . ' + + $buffer = false; + while ($textPos < $textLength) { + ' . (!empty($partSource) ? sprintf('$part = %s;', $partSource) : '') . ' + ' . (!empty($letterSource) ? sprintf('$letter = %s;', $letterSource) : '') . ' +' . $conditionsSource . ' + $buffer .= ' . $bufferSource . '; + $textPos++; + } + return array(-1, -1, $buffer); + } +'; + // Removes traling whitespaces and unnecessary empty lines + $source = preg_replace('~\n{3,}~', "\n\n", preg_replace('~\t+\n~', "\n", $source)); + + return $source; + } + + /** + * Optimizes the lexer definition. + * + * @return \FSHL\Generator + * @throws \RuntimeException If the lexer definition is wrong. + */ + private function optimize() + { + $i = 0; + foreach (array_keys($this->lexer->getStates()) as $stateName) { + if (self::STATE_QUIT === $stateName) { + continue; + } + $this->states[$stateName] = $i; + $i++; + } + $this->states[self::STATE_RETURN] = $i++; + $this->states[self::STATE_QUIT] = $i++; + + foreach ($this->lexer->getStates() as $stateName => $state) { + $stateId = $this->states[$stateName]; + + $this->classes[$stateId] = $state[self::STATE_INDEX_CLASS]; + $this->flags[$stateId] = $state[self::STATE_INDEX_FLAGS]; + $this->data[$stateId] = $state[self::STATE_INDEX_DATA]; + + if (is_array($state[self::STATE_INDEX_DIAGRAM])) { + $i = 0; + foreach ($state[self::STATE_INDEX_DIAGRAM] as $delimiter => $trans) { + $transName = $trans[self::STATE_DIAGRAM_INDEX_STATE]; + if (self::STATE_SELF === $transName) { + $transName = array_search($stateId, $this->states); + } + if (!isset($this->states[$transName])) { + throw new \RuntimeException(sprintf('Unknown state in transition %s [%s] => %s', $stateName, $delimiter, $transName)); + } + $this->delimiters[$stateId][$i] = $delimiter; + $trans[self::STATE_DIAGRAM_INDEX_STATE] = $this->states[$transName]; + $this->trans[$stateId][$i] = $trans; + $i++; + } + } else { + $this->delimiters[$stateId] = null; + $this->trans[$stateId] = null; + } + } + + if (!isset($this->states[$this->lexer->getInitialState()])) { + throw new \RuntimeException(sprintf('Unknown initial state "%s"', $this->lexer->getInitialState())); + } + + return $this; + } + + /** + * Returns a variable source. + * + * @param string $name + * @param mixed $value + * @return string + */ + private function getVarSource($name, $value) + { + return sprintf("\t\t%s = %s;\n", $name, $this->getVarValueSource($value)); + } + + /** + * Returns a variable value source. + * + * @param mixed $value + * @param integer $level + * @return string + */ + private function getVarValueSource($value, $level = 0) + { + if (is_array($value)) { + $tmp = ''; + $line = 0; + $total = 0; + foreach ($value as $k => $v) { + if ($line > 25) { + $tmp .= ",\n\t\t" . str_repeat("\t", $level); + $line = 0; + } elseif (0 !== $total) { + $tmp .= ', '; + } + $tmp .= $this->getVarValueSource($k, $level + 1) . ' => ' . $this->getVarValueSource($v, $level + 1); + + $line++; + $total++; + } + return "array(\n\t\t\t" . str_repeat("\t", $level) . $tmp . "\n" . str_repeat("\t", $level) . "\t\t)"; + } elseif (is_string($value) && preg_match('~[\\t\\n\\r]~', $value)) { + $export = var_export($value, true); + $export = str_replace(array("\t", "\n", "\r"), array('\t', '\n', '\r'), $export); + return '"' . substr($export, 1, -1) . '"'; + } else { + return var_export($value, true); + } + } +} diff --git a/apigen/libs/FSHL/FSHL/Highlighter.php b/apigen/libs/FSHL/FSHL/Highlighter.php new file mode 100644 index 00000000000..04e7b42c7a3 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Highlighter.php @@ -0,0 +1,482 @@ +setOutput($output) + ->setOptions($options, $tabIndentWidth); + } + + /** + * Highlightes a string. + * + * @param string $text + * @param \FSHL\Lexer $lexer + * @return string + * @throws \RuntimeException If no lexer is set. + */ + public function highlight($text, Lexer $lexer = null) + { + // Sets the lexer + $initialLexer = $this->lexer; + if (null !== $lexer) { + $this->setLexer($lexer); + } + + // No lexer + if (null === $this->lexer) { + throw new \RuntimeException('No lexer set'); + } + + // Prepares the text + $text = str_replace(array("\r\n", "\r"), "\n", (string) $text); + $textLength = strlen($text); + $textPos = 0; + + // Parses the text + $output = array(); + $fragment = ''; + $maxLineWidth = 0; + $line = 1; + $char = 0; + if ($this->options & self::OPTION_LINE_COUNTER) { + // Right aligment of line counter + $maxLineWidth = strlen(substr_count($text, "\n") + 1); + $fragment .= $this->line($line, $maxLineWidth); + } + $newLexerName = $lexerName = $this->lexer->language; + $newState = $state = $this->lexer->initialState; + $this->stack = array(); + + while (true) { + list($transitionId, $delimiter, $buffer) = $this->lexer->{'findDelimiter' . $state}($text, $textLength, $textPos); + + // Some data may be collected before getPart reaches the delimiter, we must output this before other processing + if (false !== $buffer) { + $bufferLength = strlen($buffer); + $textPos += $bufferLength; + $char += $bufferLength; + $fragment .= $this->template($buffer, $state); + if (isset($fragment[8192])) { + $output[] = $fragment; + $fragment = ''; + } + } + + if (-1 === $transitionId) { + // End of stream + break; + } + + // Processes received delimiter as string + $prevLine = $line; + $prevChar = $char; + $prevTextPos = $textPos; + + $delimiterLength = strlen($delimiter); + $textPos += $delimiterLength; + $char += $delimiterLength; + + // Adds line counter and tab indentation + $addLine = false; + if ("\n" === $delimiter[$delimiterLength - 1]) { + // Line counter + $line++; + $char = 0; + if ($this->options & self::OPTION_LINE_COUNTER) { + $addLine = true; + $actualLine = $line; + } + } elseif ("\t" === $delimiter && ($this->options & self::OPTION_TAB_INDENT)) { + // Tab indentation + $i = $char % $this->tabIndentWidth; + $delimiter = $this->tabs[$i][0]; + $char += $this->tabs[$i][1]; + } + + // Gets new state from the transitions table + $newState = $this->lexer->trans[$state][$transitionId][Generator::STATE_DIAGRAM_INDEX_STATE]; + if ($newState === $this->lexer->returnState) { + // Chooses mode of delimiter processing + if (Generator::BACK === $this->lexer->trans[$state][$transitionId][Generator::STATE_DIAGRAM_INDEX_MODE]) { + $line = $prevLine; + $char = $prevChar; + $textPos = $prevTextPos; + } else { + $fragment .= $this->template($delimiter, $state); + if ($addLine) { + $fragment .= $this->line($actualLine, $maxLineWidth); + } + if (isset($fragment[8192])) { + $output[] = $fragment; + $fragment = ''; + } + } + + // Get state from the context stack + if ($item = $this->popState()) { + list($newLexerName, $state) = $item; + // If previous context was in a different lexer, switch the lexer too + if ($newLexerName !== $lexerName) { + $this->setLexerByName($newLexerName); + $lexerName = $newLexerName; + } + } else { + $state = $this->lexer->initialState; + } + + continue; + } + + // Chooses mode of delimiter processing + $type = $this->lexer->trans[$state][$transitionId][Generator::STATE_DIAGRAM_INDEX_MODE]; + if (Generator::BACK === $type) { + $line = $prevLine; + $char = $prevChar; + $textPos = $prevTextPos; + } else { + $fragment .= $this->template($delimiter, Generator::NEXT === $type ? $newState : $state); + if ($addLine) { + $fragment .= $this->line($actualLine, $maxLineWidth); + } + if (isset($fragment[8192])) { + $output[] = $fragment; + $fragment = ''; + } + } + + // Switches between lexers (transition to embedded language) + if ($this->lexer->flags[$newState] & Generator::STATE_FLAG_NEWLEXER) { + if ($newState === $this->lexer->quitState) { + // Returns to the previous lexer + if ($item = $this->popState()) { + list($newLexerName, $state) = $item; + if ($newLexerName !== $lexerName) { + $this->setLexerByName($newLexerName); + $lexerName = $newLexerName; + } + } else { + $state = $this->lexer->initialState; + } + } else { + // Switches to the embedded language + $newLexerName = $this->lexer->data[$newState]; + $this->pushState($lexerName, $this->lexer->trans[$newState] ? $newState : $state); + $this->setLexerByName($newLexerName); + $lexerName = $newLexerName; + $state = $this->lexer->initialState; + } + + continue; + } + + // If newState is marked with recursion flag (alias call), push current state to the context stack + if (($this->lexer->flags[$newState] & Generator::STATE_FLAG_RECURSION) && $state !== $newState) { + $this->pushState($lexerName, $state); + } + + // Change the state + $state = $newState; + } + + // Adds template end + $fragment .= $this->output->template('', null); + $output[] = $fragment; + + // Restore lexer + $this->lexer = $initialLexer; + + return implode('', $output); + } + + /** + * Sets the output mode. + * + * @param \FSHL\Output $output + * @return \FSHL\Highlighter + */ + public function setOutput(Output $output) + { + $this->output = $output; + + return $this; + } + + /** + * Sets options. + * + * @param integer $options + * @param integer $tabIndentWidth + * @return \FSHL\Highlighter + */ + public function setOptions($options = self::OPTION_DEFAULT, $tabIndentWidth = 4) + { + $this->options = $options; + + if (($this->options & self::OPTION_TAB_INDENT) && $tabIndentWidth > 0) { + // Precalculate a table for tab indentation + $t = ' '; + $ti = 0; + for ($i = $tabIndentWidth; $i; $i--) { + $this->tabs[$i % $tabIndentWidth] = array($t, $ti++); + $t .= ' '; + } + $this->tabIndentWidth = $tabIndentWidth; + } else { + $this->options &= ~self::OPTION_TAB_INDENT; + } + + return $this; + } + + /** + * Sets the default lexer. + * + * @param \FSHL\Lexer $lexer + * @return \FSHL\Highlighter + */ + public function setLexer(Lexer $lexer) + { + // Generates the lexer cache on fly, if the lexer cache doesn't exist + if (!$this->findCache($lexer->getLanguage())) { + $this->generateCache($lexer); + } + + return $this; + } + + /** + * Sets the current lexer by name. + * + * @param string $lexerName + * @return \FSHL\Highlighter + * @throws \InvalidArgumentException If the class for given lexer doesn't exist. + */ + private function setLexerByName($lexerName) + { + // Generates the lexer cache on fly, if the lexer cache doesn't exist + if (!$this->findCache($lexerName)) { + // Finds the lexer + $lexerClass = '\\FSHL\\Lexer\\' . $lexerName; + if (!class_exists($lexerClass)) { + throw new \InvalidArgumentException(sprintf('The class for "%s" lexer doesn\'t exist', $lexerName)); + } + $lexer = new $lexerClass(); + + // Generates the lexer cache on fly + $this->generateCache($lexer); + } + + return $this; + } + + /** + * Tries to find the lexer cache. + * + * @param string $lexerName + * @return boolean + */ + private function findCache($lexerName) + { + // Lexer has been used before + if (isset($this->lexers[$lexerName])) { + $this->lexer = $this->lexers[$lexerName]; + return true; + } + + // Loads lexer cache + $lexerCacheClass = '\\FSHL\\Lexer\\Cache\\' . $lexerName; + if (class_exists($lexerCacheClass)) { + $this->lexers[$lexerName] = new $lexerCacheClass(); + $this->lexer = $this->lexers[$lexerName]; + return true; + } + + return false; + } + + /** + * Generates the lexer cache on fly. + * + * @param \FSHL\Lexer $lexer + * @return \FSHL\Highlighter + */ + private function generateCache(Lexer $lexer) + { + $generator = new Generator($lexer); + try { + $generator->saveToCache(); + } catch (\RuntimeException $e) { + $file = tempnam(sys_get_temp_dir(), 'fshl'); + file_put_contents($file, $generator->getSource()); + require_once $file; + unlink($file); + } + + $lexerName = $lexer->getLanguage(); + $lexerCacheClass = '\\FSHL\\Lexer\\Cache\\' . $lexerName; + $this->lexers[$lexerName] = new $lexerCacheClass(); + $this->lexer = $this->lexers[$lexerName]; + + return $this; + } + + /** + * Outputs a word. + * + * @param string $part + * @param string $state + * @return string + */ + private function template($part, $state) + { + if ($this->lexer->flags[$state] & Generator::STATE_FLAG_KEYWORD) { + $normalized = Generator::CASE_SENSITIVE === $this->lexer->keywords[Generator::KEYWORD_INDEX_CASE_SENSITIVE] ? $part : strtolower($part); + + if (isset($this->lexer->keywords[Generator::KEYWORD_INDEX_LIST][$normalized])) { + return $this->output->keyword($part, $this->lexer->keywords[Generator::KEYWORD_INDEX_CLASS] . $this->lexer->keywords[Generator::KEYWORD_INDEX_LIST][$normalized]); + } + } + + return $this->output->template($part, $this->lexer->classes[$state]); + } + + /** + * Outputs a line. + * + * @param integer $line + * @param integer $maxLineWidth + * @return string + */ + private function line($line, $maxLineWidth) + { + return $this->output->template(str_pad($line, $maxLineWidth, ' ', STR_PAD_LEFT) . ': ', 'line'); + } + + /** + * Pushes a state to the context stack. + * + * @param string $lexerName + * @param string $state + * @return \FSHL\Highlighter + */ + private function pushState($lexerName, $state) + { + array_unshift($this->stack, array($lexerName, $state)); + return $this; + } + + /** + * Pops a state from the context stack. + * + * @return array|null + */ + private function popState() + { + return array_shift($this->stack); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer.php b/apigen/libs/FSHL/FSHL/Lexer.php new file mode 100644 index 00000000000..a78a5baadb9 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer.php @@ -0,0 +1,66 @@ +language = 'Cpp'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 0, 1 => 1 + ), 1 => array( + 0 => 0, 1 => 1 + ), 2 => array( + 0 => 1, 1 => -1 + ), 3 => array( + 0 => 8, 1 => 1 + ), 4 => array( + 0 => 4, 1 => 1 + ), 5 => array( + 0 => 2, 1 => 1 + ), 6 => array( + 0 => 2, 1 => 1 + ), 7 => array( + 0 => 5, 1 => 1 + ), 8 => array( + 0 => 6, 1 => 1 + ), 9 => array( + 0 => 7, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 9, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 9, 1 => -1 + ) + ), 3 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 9, 1 => -1 + ) + ), 4 => array( + 0 => array( + 0 => 4, 1 => 1 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 9, 1 => -1 + ) + ), 5 => array( + 0 => array( + 0 => 9, 1 => 0 + ), 1 => array( + 0 => 5, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ), 4 => array( + 0 => 5, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 9, 1 => 0 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 6, 1 => 1 + ), 3 => array( + 0 => 6, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 7, 1 => 1 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 9, 1 => 0 + ) + ), 8 => array( + 0 => array( + 0 => 9, 1 => -1 + ), 1 => array( + 0 => 8, 1 => 1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 9; + $this->quitState = 10; + $this->flags = array( + 0 => 0, 1 => 5, 2 => 4, 3 => 0, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 4 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => NULL, 2 => 'cpp-num', 3 => 'cpp-num', 4 => 'cpp-preproc', 5 => 'cpp-quote', 6 => 'cpp-quote', 7 => 'cpp-comment', 8 => 'cpp-comment' + ); + $this->keywords = array( + 0 => 'cpp-keywords', 1 => array( + 'bool' => 1, 'break' => 1, 'case' => 1, 'catch' => 1, 'char' => 1, 'class' => 1, 'const' => 1, 'const_cast' => 1, 'continue' => 1, 'default' => 1, 'delete' => 1, 'deprecated' => 1, 'dllexport' => 1, 'dllimport' => 1, 'do' => 1, 'double' => 1, 'dynamic_cast' => 1, 'else' => 1, 'enum' => 1, 'explicit' => 1, 'extern' => 1, 'false' => 1, 'float' => 1, 'for' => 1, 'friend' => 1, 'goto' => 1, + 'if' => 1, 'inline' => 1, 'int' => 1, 'long' => 1, 'mutable' => 1, 'naked' => 1, 'namespace' => 1, 'new' => 1, 'noinline' => 1, 'noreturn' => 1, 'nothrow' => 1, 'novtable' => 1, 'operator' => 1, 'private' => 1, 'property' => 1, 'protected' => 1, 'public' => 1, 'register' => 1, 'reinterpret_cast' => 1, 'return' => 1, 'selectany' => 1, 'short' => 1, 'signed' => 1, 'sizeof' => 1, 'static' => 1, 'static_cast' => 1, + 'struct' => 1, 'switch' => 1, 'template' => 1, 'this' => 1, 'thread' => 1, 'throw' => 1, 'true' => 1, 'try' => 1, 'typedef' => 1, 'typeid' => 1, 'typename' => 1, 'union' => 1, 'unsigned' => 1, 'using' => 1, 'uuid' => 1, 'virtual' => 1, 'void' => 1, 'volatile' => 1, '__wchar_t' => 1, 'wchar_t' => 1, 'while' => 1, '__abstract' => 1, '__alignof' => 1, '__asm' => 1, '__assume' => 1, '__based' => 1, + '__box' => 1, '__cdecl' => 1, '__declspec' => 1, '__delegate' => 1, '__event' => 1, '__except' => 1, '__fastcall' => 1, '__finally' => 1, '__forceinline' => 1, '__gc' => 1, '__hook' => 1, '__identifier' => 1, '__if_exists' => 1, '__if_not_exists' => 1, '__inline' => 1, '__int8' => 1, '__int16' => 1, '__int32' => 1, '__int64' => 1, '__interface' => 1, '__leave' => 1, '__m64' => 1, '__m128' => 1, '__m128d' => 1, '__m128i' => 1, '__multiple_inheritance' => 1, + '__nogc' => 1, '__noop' => 1, '__pin' => 1, '__property' => 1, '__raise' => 1, '__sealed' => 1, '__single_inheritance' => 1, '__stdcall' => 1, '__super' => 1, '__try_cast' => 1, '__try' => 1, '__unhook' => 1, '__uuidof' => 1, '__value' => 1, '__virtual_inheritance' => 1, '__w64' => 1 + ), 2 => true + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 3 => '//', 4 => '#', 7 => '"', 8 => '\'', 9 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^[a-z]+~i', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if (preg_match('~^\\d+~', $part, $matches)) { + return array(5, $matches[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(6, $matches[0], $buffer); + } + if ($delimiters[7] === $letter) { + return array(7, $delimiters[7], $buffer); + } + if ($delimiters[8] === $letter) { + return array(8, $delimiters[8], $buffer); + } + if (0 === strpos($part, $delimiters[9])) { + return array(9, $delimiters[9], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state KEYWORD. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'x', 1 => 'f' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + return array(3, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'L' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state PREPROC. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\\\n", 1 => "\t", 2 => "\\\r\n", 3 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 1 => '\\\\', 2 => '\\"', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 1 => '\\\'', 2 => "\n", 3 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_BLOCK. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Css.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Css.php new file mode 100644 index 00000000000..8108bfa86ea --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Css.php @@ -0,0 +1,794 @@ +language = 'Css'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 3, 1 => 1 + ), 2 => array( + 0 => 3, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 5, 1 => 1 + ), 5 => array( + 0 => 7, 1 => 1 + ), 6 => array( + 0 => 12, 1 => 1 + ), 7 => array( + 0 => 1, 1 => 1 + ), 8 => array( + 0 => 2, 1 => 1 + ), 9 => array( + 0 => 0, 1 => 1 + ), 10 => array( + 0 => 0, 1 => 1 + ), 11 => array( + 0 => 15, 1 => 1 + ), 12 => array( + 0 => 13, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 0 + ), 2 => array( + 0 => 1, 1 => 0 + ), 3 => array( + 0 => 1, 1 => 1 + ), 4 => array( + 0 => 1, 1 => 1 + ), 5 => array( + 0 => 14, 1 => 0 + ), 6 => array( + 0 => 12, 1 => 1 + ) + ), 2 => array( + 0 => array( + 0 => 14, 1 => -1 + ), 1 => array( + 0 => 12, 1 => 1 + ) + ), 3 => array( + 0 => array( + 0 => 14, 1 => 1 + ), 1 => array( + 0 => 14, 1 => -1 + ), 2 => array( + 0 => 14, 1 => -1 + ), 3 => array( + 0 => 6, 1 => 1 + ), 4 => array( + 0 => 12, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 14, 1 => -1 + ), 1 => array( + 0 => 14, 1 => -1 + ), 2 => array( + 0 => 14, 1 => -1 + ), 3 => array( + 0 => 6, 1 => 1 + ), 4 => array( + 0 => 12, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 14, 1 => -1 + ), 1 => array( + 0 => 14, 1 => -1 + ), 2 => array( + 0 => 14, 1 => -1 + ), 3 => array( + 0 => 6, 1 => 1 + ), 4 => array( + 0 => 12, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 14, 1 => -1 + ), 1 => array( + 0 => 14, 1 => -1 + ) + ), 7 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 0 + ), 2 => array( + 0 => 7, 1 => 0 + ), 3 => array( + 0 => 7, 1 => 1 + ), 4 => array( + 0 => 7, 1 => 1 + ), 5 => array( + 0 => 14, 1 => 0 + ), 6 => array( + 0 => 12, 1 => 1 + ) + ), 8 => array( + 0 => array( + 0 => 14, 1 => -1 + ), 1 => array( + 0 => 14, 1 => -1 + ), 2 => array( + 0 => 8, 1 => 1 + ), 3 => array( + 0 => 8, 1 => 1 + ), 4 => array( + 0 => 12, 1 => 1 + ) + ), 9 => array( + 0 => array( + 0 => 11, 1 => 1 + ), 1 => array( + 0 => 14, 1 => -1 + ), 2 => array( + 0 => 10, 1 => 1 + ), 3 => array( + 0 => 14, 1 => -1 + ), 4 => array( + 0 => 14, 1 => -1 + ), 5 => array( + 0 => 9, 1 => 1 + ), 6 => array( + 0 => 9, 1 => 1 + ), 7 => array( + 0 => 12, 1 => 1 + ) + ), 10 => array( + 0 => array( + 0 => 14, 1 => 0 + ), 1 => array( + 0 => 9, 1 => 1 + ) + ), 11 => array( + 0 => array( + 0 => 14, 1 => -1 + ) + ), 12 => array( + 0 => array( + 0 => 12, 1 => 1 + ), 1 => array( + 0 => 12, 1 => 1 + ), 2 => array( + 0 => 14, 1 => 0 + ) + ), 13 => NULL, 15 => NULL + ); + $this->initialState = 0; + $this->returnState = 14; + $this->quitState = 15; + $this->flags = array( + 0 => 0, 1 => 4, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 4, 9 => 4, 10 => 4, 11 => 4, 12 => 4, 13 => 8, 15 => 8 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL, 11 => NULL, 12 => NULL, 13 => 'Php', 15 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => 'css-at-rule', 2 => 'css-at-rule', 3 => 'css-tag', 4 => 'css-id', 5 => 'css-class', 6 => 'css-pseudo', 7 => NULL, 8 => 'css-property', 9 => 'css-value', 10 => 'css-func', 11 => 'css-color', 12 => 'css-comment', 13 => 'xlang', 15 => 'html-tag' + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 2 => '*', 3 => '#', 4 => '.', 5 => '{', 6 => '/*', 7 => '@media', 8 => '@', 9 => "\n", 10 => "\t", 11 => ' ':', 2 => ';', 3 => "\n", 4 => "\t", 5 => ')', 6 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~[-a-z]+~iA', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state AT_RULE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\s+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state TAG. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '{', 1 => ',', 3 => ':', 4 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^\s+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state ID. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '{', 1 => ',', 3 => ':', 4 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^\s+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state CLASS. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '{', 2 => ',', 3 => ':', 4 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\s+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state PSEUDO. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => ',' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^\s+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state DEF. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => ':', 2 => ';', 3 => "\n", 4 => "\t", 5 => '}', 6 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~[-a-z]+~iA', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state PROPERTY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => ':', 1 => '}', 2 => "\n", 3 => "\t", 4 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state VALUE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '#', 1 => ';', 3 => ')', 4 => '}', 5 => "\n", 6 => "\t", 7 => '/*' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~[a-z]+\s*\(~iA', $text, $matches, 0, $textPos)) { + return array(2, $matches[0], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if ($delimiters[6] === $letter) { + return array(6, $delimiters[6], $buffer); + } + if (0 === strpos($part, $delimiters[7])) { + return array(7, $delimiters[7], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state FUNC. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter10($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => ')' + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COLOR. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter11($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter12($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Html.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Html.php new file mode 100644 index 00000000000..5fa9997bcff --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Html.php @@ -0,0 +1,672 @@ +language = 'Html'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 0, 1 => 0 + ), 3 => array( + 0 => 2, 1 => 1 + ), 4 => array( + 0 => 1, 1 => 1 + ), 5 => array( + 0 => 0, 1 => 1 + ), 6 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 0, 1 => 0 + ), 2 => array( + 0 => 0, 1 => 0 + ) + ), 2 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 3, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 0 + ), 3 => array( + 0 => 4, 1 => 0 + ), 4 => array( + 0 => 6, 1 => 0 + ), 5 => array( + 0 => 6, 1 => 0 + ), 6 => array( + 0 => 11, 1 => 1 + ) + ), 3 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 2, 1 => -1 + ), 3 => array( + 0 => 2, 1 => -1 + ), 4 => array( + 0 => 11, 1 => 1 + ), 5 => array( + 0 => 3, 1 => 1 + ), 6 => array( + 0 => 3, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 11, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 3, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 12, 1 => 0 + ) + ), 6 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 11, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 3, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 12, 1 => 0 + ) + ), 8 => array( + 0 => array( + 0 => 12, 1 => 0 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 8, 1 => 1 + ), 3 => array( + 0 => 8, 1 => 1 + ) + ), 9 => array( + 0 => array( + 0 => 12, 1 => 0 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 9, 1 => 1 + ), 3 => array( + 0 => 9, 1 => 1 + ) + ), 10 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 10, 1 => 1 + ), 2 => array( + 0 => 0, 1 => 0 + ), 3 => array( + 0 => 11, 1 => 1 + ) + ), 11 => NULL + ); + $this->initialState = 0; + $this->returnState = 12; + $this->quitState = 13; + $this->flags = array( + 0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 8, 6 => 0, 7 => 8, 8 => 4, 9 => 4, 10 => 0, 11 => 8 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => 'Css', 6 => NULL, 7 => 'Javascript', 8 => NULL, 9 => NULL, 10 => NULL, 11 => 'Php' + ); + $this->classes = array( + 0 => NULL, 1 => 'html-entity', 2 => 'html-tag', 3 => 'html-tagin', 4 => 'html-tagin', 5 => 'html-tag', 6 => 'html-tagin', 7 => 'html-tag', 8 => 'html-quote', 9 => 'html-quote', 10 => 'html-comment', 11 => 'xlang' + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (preg_match('~<\\?(php|=|(?!xml))~A', $text, $matches, 0, $textPos)) { + return array(3, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/HtmlOnly.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/HtmlOnly.php new file mode 100644 index 00000000000..338d463129e --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/HtmlOnly.php @@ -0,0 +1,706 @@ +language = 'HtmlOnly'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 12, 1 => 1 + ), 1 => array( + 0 => 0, 1 => 0 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 1, 1 => 1 + ), 4 => array( + 0 => 0, 1 => 1 + ), 5 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 0, 1 => 0 + ), 2 => array( + 0 => 0, 1 => 0 + ) + ), 2 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 3, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 0 + ), 3 => array( + 0 => 4, 1 => 0 + ), 4 => array( + 0 => 7, 1 => 0 + ), 5 => array( + 0 => 7, 1 => 0 + ) + ), 3 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 2, 1 => -1 + ), 3 => array( + 0 => 2, 1 => -1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 3, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 5, 1 => -1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 4, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 6, 1 => 0 + ) + ), 6 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 2, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 1 + ), 2 => array( + 0 => 8, 1 => -1 + ), 3 => array( + 0 => 7, 1 => 1 + ), 4 => array( + 0 => 7, 1 => 1 + ) + ), 8 => array( + 0 => array( + 0 => 9, 1 => 0 + ) + ), 9 => array( + 0 => array( + 0 => 9, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 2, 1 => 1 + ) + ), 10 => array( + 0 => array( + 0 => 13, 1 => 0 + ) + ), 11 => array( + 0 => array( + 0 => 13, 1 => 0 + ) + ), 12 => array( + 0 => array( + 0 => 12, 1 => 1 + ), 1 => array( + 0 => 12, 1 => 1 + ), 2 => array( + 0 => 0, 1 => 0 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 13; + $this->quitState = 14; + $this->flags = array( + 0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 4, 11 => 4, 12 => 0 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL, 11 => NULL, 12 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => 'html-entity', 2 => 'html-tag', 3 => 'html-tagin', 4 => 'html-tagin', 5 => 'html-tag', 6 => NULL, 7 => 'html-tagin', 8 => 'html-tag', 9 => NULL, 10 => 'html-quote', 11 => 'html-quote', 12 => 'html-comment' + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Java.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Java.php new file mode 100644 index 00000000000..23eb3f20b16 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Java.php @@ -0,0 +1,479 @@ +language = 'Java'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 1, 1 => -1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 5, 1 => 1 + ), 5 => array( + 0 => 6, 1 => 1 + ), 6 => array( + 0 => 7, 1 => 1 + ), 7 => array( + 0 => 0, 1 => 1 + ), 8 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 8, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 8, 1 => -1 + ) + ), 3 => array( + 0 => array( + 0 => 8, 1 => -1 + ) + ), 4 => array( + 0 => array( + 0 => 8, 1 => 0 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 4, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 8, 1 => 0 + ), 1 => array( + 0 => 5, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ), 4 => array( + 0 => 5, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 8, 1 => 0 + ) + ), 7 => array( + 0 => array( + 0 => 8, 1 => -1 + ), 1 => array( + 0 => 7, 1 => 1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 8; + $this->quitState = 9; + $this->flags = array( + 0 => 0, 1 => 5, 2 => 4, 3 => 0, 4 => 4, 5 => 4, 6 => 4, 7 => 4 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => NULL, 2 => 'java-num', 3 => 'java-num', 4 => 'java-quote', 5 => 'java-quote', 6 => 'java-comment', 7 => 'java-comment' + ); + $this->keywords = array( + 0 => 'java-keywords', 1 => array( + 'abstract' => 1, 'double' => 1, 'int' => 1, 'strictfp' => 1, 'boolean' => 1, 'else' => 1, 'interface' => 1, 'super' => 1, 'break' => 1, 'extends' => 1, 'long' => 1, 'switch' => 1, 'byte' => 1, 'final' => 1, 'native' => 1, 'synchronized' => 1, 'case' => 1, 'finally' => 1, 'new' => 1, 'this' => 1, 'catch' => 1, 'float' => 1, 'package' => 1, 'throw' => 1, 'char' => 1, 'for' => 1, + 'private' => 1, 'throws' => 1, 'class' => 1, 'goto' => 1, 'protected' => 1, 'transient' => 1, 'const' => 1, 'if' => 1, 'public' => 1, 'try' => 1, 'continue' => 1, 'implements' => 1, 'return' => 1, 'void' => 1, 'default' => 1, 'import' => 1, 'short' => 1, 'volatile' => 1, 'do' => 1, 'instanceof' => 1, 'static' => 1, 'while' => 1 + ), 2 => true + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 3 => '"', 4 => '\'', 5 => '/*', 6 => '//', 7 => "\n", 8 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^[a-z]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if (preg_match('~^\\d+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if (0 === strpos($part, $delimiters[5])) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + if ($delimiters[7] === $letter) { + return array(7, $delimiters[7], $buffer); + } + if ($delimiters[8] === $letter) { + return array(8, $delimiters[8], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state KEYWORD. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'x' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + return array(2, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 1 => '\\\\', 2 => '\\"', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 1 => '\\\\', 2 => '\\\'', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_BLOCK. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Javascript.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Javascript.php new file mode 100644 index 00000000000..fedf6079e40 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Javascript.php @@ -0,0 +1,507 @@ +language = 'Javascript'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 0, 1 => 1 + ), 1 => array( + 0 => 0, 1 => 1 + ), 2 => array( + 0 => 1, 1 => -1 + ), 3 => array( + 0 => 2, 1 => 1 + ), 4 => array( + 0 => 2, 1 => 1 + ), 5 => array( + 0 => 1, 1 => 0 + ), 6 => array( + 0 => 4, 1 => 1 + ), 7 => array( + 0 => 5, 1 => 1 + ), 8 => array( + 0 => 6, 1 => 1 + ), 9 => array( + 0 => 7, 1 => 1 + ), 10 => array( + 0 => 8, 1 => 1 + ), 11 => array( + 0 => 9, 1 => 1 + ), 12 => array( + 0 => 11, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 10, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 10, 1 => -1 + ) + ), 3 => array( + 0 => array( + 0 => 10, 1 => -1 + ) + ), 4 => array( + 0 => array( + 0 => 10, 1 => 0 + ), 1 => array( + 0 => 9, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 10, 1 => 0 + ), 1 => array( + 0 => 9, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 10, 1 => 0 + ), 3 => array( + 0 => 9, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 10, 1 => -1 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 9, 1 => 1 + ) + ), 8 => array( + 0 => array( + 0 => 10, 1 => -1 + ) + ), 9 => NULL, 11 => NULL + ); + $this->initialState = 0; + $this->returnState = 10; + $this->quitState = 11; + $this->flags = array( + 0 => 0, 1 => 5, 2 => 4, 3 => 0, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 0, 9 => 8, 11 => 8 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => 'Php', 11 => NULL + ); + $this->classes = array( + 0 => 'js-out', 1 => 'js-out', 2 => 'js-num', 3 => 'js-num', 4 => 'js-quote', 5 => 'js-quote', 6 => 'js-comment', 7 => 'js-comment', 8 => 'js-quote', 9 => 'xlang', 11 => 'html-tag' + ); + $this->keywords = array( + 0 => 'js-keywords', 1 => array( + 'abstract' => 1, 'boolean' => 1, 'break' => 1, 'byte' => 1, 'case' => 1, 'catch' => 1, 'char' => 1, 'class' => 1, 'const' => 1, 'continue' => 1, 'debugger' => 1, 'default' => 1, 'delete' => 1, 'do' => 1, 'double' => 1, 'else' => 1, 'enum' => 1, 'export' => 1, 'extends' => 1, 'false' => 1, 'final' => 1, 'finally' => 1, 'float' => 1, 'for' => 1, 'function' => 1, 'goto' => 1, + 'if' => 1, 'implements' => 1, 'import' => 1, 'in' => 1, 'instanceof' => 1, 'int' => 1, 'interface' => 1, 'long' => 1, 'native' => 1, 'new' => 1, 'null' => 1, 'package' => 1, 'private' => 1, 'protected' => 1, 'public' => 1, 'return' => 1, 'short' => 1, 'static' => 1, 'super' => 1, 'switch' => 1, 'synchronized' => 1, 'this' => 1, 'throw' => 1, 'throws' => 1, 'transient' => 1, 'true' => 1, + 'try' => 1, 'typeof' => 1, 'var' => 1, 'void' => 1, 'volatile' => 1, 'while' => 1, 'with' => 1, 'document' => 2, 'getAttribute' => 2, 'getElementsByTagName' => 2, 'getElementById' => 2 + ), 2 => true + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 5 => '.', 6 => '"', 7 => '\'', 8 => '/*', 9 => '//', 12 => ' 'x' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + return array(2, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"' + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~<\\?(php|=|(?!xml))~A', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'' + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~<\\?(php|=|(?!xml))~A', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_BLOCK. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (preg_match('~<\\?(php|=|(?!xml))~A', $text, $matches, 0, $textPos)) { + return array(3, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~<\\?(php|=|(?!xml))~A', $text, $matches, 0, $textPos)) { + return array(2, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state REGEXP. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Minimal.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Minimal.php new file mode 100644 index 00000000000..c9a1fd30d5e --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Minimal.php @@ -0,0 +1,162 @@ +language = 'Minimal'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 0, 1 => 1 + ), 1 => array( + 0 => 0, 1 => 1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 1; + $this->quitState = 2; + $this->flags = array( + 0 => 0 + ); + $this->data = array( + 0 => NULL + ); + $this->classes = array( + 0 => NULL + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Neon.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Neon.php new file mode 100644 index 00000000000..ec7f74e30a1 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Neon.php @@ -0,0 +1,710 @@ +language = 'Neon'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 1, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 3, 1 => 1 + ), 4 => array( + 0 => 0, 1 => 1 + ), 5 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 1, 1 => 1 + ), 3 => array( + 0 => 2, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 1, 1 => 1 + ), 6 => array( + 0 => 1, 1 => 1 + ) + ), 2 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ) + ), 3 => array( + 0 => array( + 0 => 4, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 0, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 8, 1 => 1 + ), 4 => array( + 0 => 9, 1 => 1 + ), 5 => array( + 0 => 6, 1 => 1 + ), 6 => array( + 0 => 6, 1 => 1 + ), 7 => array( + 0 => 6, 1 => 1 + ), 8 => array( + 0 => 6, 1 => 1 + ), 9 => array( + 0 => 6, 1 => 1 + ), 10 => array( + 0 => 6, 1 => 1 + ), 11 => array( + 0 => 6, 1 => 1 + ), 12 => array( + 0 => 5, 1 => 1 + ), 13 => array( + 0 => 11, 1 => 1 + ), 14 => array( + 0 => 11, 1 => 1 + ), 15 => array( + 0 => 10, 1 => 1 + ), 16 => array( + 0 => 12, 1 => 1 + ), 17 => array( + 0 => 4, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 10, 1 => 1 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 0, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 13, 1 => -1 + ) + ), 7 => array( + 0 => array( + 0 => 0, 1 => -1 + ), 1 => array( + 0 => 7, 1 => 1 + ) + ), 8 => array( + 0 => array( + 0 => 13, 1 => 0 + ), 1 => array( + 0 => 10, 1 => 1 + ), 2 => array( + 0 => 8, 1 => 1 + ) + ), 9 => array( + 0 => array( + 0 => 13, 1 => 0 + ), 1 => array( + 0 => 10, 1 => 1 + ), 2 => array( + 0 => 9, 1 => 1 + ) + ), 10 => array( + 0 => array( + 0 => 13, 1 => 0 + ) + ), 11 => array( + 0 => array( + 0 => 11, 1 => 1 + ), 1 => array( + 0 => 13, 1 => -1 + ) + ), 12 => array( + 0 => array( + 0 => 13, 1 => -1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 13; + $this->quitState = 14; + $this->flags = array( + 0 => 0, 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0, 6 => 4, 7 => 0, 8 => 4, 9 => 4, 10 => 4, 11 => 4, 12 => 4 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL, 11 => NULL, 12 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => 'neon-section', 2 => 'neon-key', 3 => 'neon-sep', 4 => 'neon-value', 5 => 'neon-value', 6 => 'neon-sep', 7 => 'neon-comment', 8 => 'neon-quote', 9 => 'neon-quote', 10 => 'neon-var', 11 => 'neon-num', 12 => 'neon-ref' + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 2 => '#', 3 => '-', 4 => "\n", 5 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if (preg_match('~[\\w.]+(?=(\\s*<\\s*[\\w.]+)?\\s*:\\s*\n)~Ai', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + if (preg_match('~[\\w.]+(?=\\s*(?::|=))~Ai', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state SECTION. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '<', 1 => ':', 4 => '-', 5 => "\n", 6 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~[\\w.]+(?=(\\s*<\\s*[\\w.]+)?\\s*:\\s*\n)~Ai', $text, $matches, 0, $textPos)) { + return array(2, $matches[0], $buffer); + } + if (preg_match('~[\\w.]+(?=\\s*(?::|=))~Ai', $text, $matches, 0, $textPos)) { + return array(3, $matches[0], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if ($delimiters[6] === $letter) { + return array(6, $delimiters[6], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state KEY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => ':', 1 => '=' + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + return array(2, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state LIST. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state VALUE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 2 => '#', 3 => '"', 4 => '\'', 5 => '[', 6 => ']', 7 => '{', 8 => '}', 9 => '=', 10 => ',', 11 => ':', 16 => '@', 17 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~[\\w.]+(?=\\s*(?::|=))~Ai', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if ($delimiters[6] === $letter) { + return array(6, $delimiters[6], $buffer); + } + if ($delimiters[7] === $letter) { + return array(7, $delimiters[7], $buffer); + } + if ($delimiters[8] === $letter) { + return array(8, $delimiters[8], $buffer); + } + if ($delimiters[9] === $letter) { + return array(9, $delimiters[9], $buffer); + } + if ($delimiters[10] === $letter) { + return array(10, $delimiters[10], $buffer); + } + if ($delimiters[11] === $letter) { + return array(11, $delimiters[11], $buffer); + } + if (preg_match('~[a-z](?![,\\]}#\n])~Ai', $text, $matches, 0, $textPos)) { + return array(12, $matches[0], $buffer); + } + if (preg_match('~^\\d+~', $part, $matches)) { + return array(13, $matches[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(14, $matches[0], $buffer); + } + if (preg_match('~%\\w+(?=%)~Ai', $text, $matches, 0, $textPos)) { + return array(15, $matches[0], $buffer); + } + if ($delimiters[16] === $letter) { + return array(16, $delimiters[16], $buffer); + } + if ($delimiters[17] === $letter) { + return array(17, $delimiters[17], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state TEXT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => '#', 2 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if (preg_match('~%\\w+(?=%)~Ai', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state SEPARATOR. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 2 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~%\\w+(?=%)~Ai', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 2 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~%\\w+(?=%)~Ai', $text, $matches, 0, $textPos)) { + return array(1, $matches[0], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state VARIABLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter10($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '%' + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter11($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state REFERENCE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter12($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Php.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Php.php new file mode 100644 index 00000000000..06bd5c76f6e --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Php.php @@ -0,0 +1,992 @@ +language = 'Php'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 5, 1 => 1 + ), 1 => array( + 0 => 10, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 2, 1 => -1 + ), 4 => array( + 0 => 2, 1 => -1 + ), 5 => array( + 0 => 13, 1 => 1 + ), 6 => array( + 0 => 13, 1 => 1 + ), 7 => array( + 0 => 0, 1 => 1 + ), 8 => array( + 0 => 0, 1 => 1 + ), 9 => array( + 0 => 4, 1 => 1 + ), 10 => array( + 0 => 3, 1 => 1 + ), 11 => array( + 0 => 4, 1 => 1 + ), 12 => array( + 0 => 17, 1 => 1 + ), 13 => array( + 0 => 1, 1 => -1 + ), 14 => array( + 0 => 11, 1 => 1 + ), 15 => array( + 0 => 8, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 16, 1 => 0 + ), 1 => array( + 0 => 16, 1 => 0 + ), 2 => array( + 0 => 16, 1 => 0 + ) + ), 2 => array( + 0 => array( + 0 => 16, 1 => -1 + ) + ), 3 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 3, 1 => 1 + ), 2 => array( + 0 => 16, 1 => 0 + ) + ), 4 => array( + 0 => array( + 0 => 16, 1 => -1 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 16, 1 => 0 + ) + ), 5 => array( + 0 => array( + 0 => 16, 1 => -1 + ), 1 => array( + 0 => 5, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 16, 1 => 0 + ), 1 => array( + 0 => 16, 1 => -1 + ) + ), 7 => array( + 0 => array( + 0 => 16, 1 => 0 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ), 4 => array( + 0 => 6, 1 => 1 + ), 5 => array( + 0 => 7, 1 => 1 + ), 6 => array( + 0 => 7, 1 => 1 + ) + ), 8 => array( + 0 => array( + 0 => 9, 1 => 1 + ), 1 => array( + 0 => 8, 1 => 1 + ), 2 => array( + 0 => 8, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ), 4 => array( + 0 => 6, 1 => 1 + ) + ), 9 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 8, 1 => -1 + ) + ), 10 => array( + 0 => array( + 0 => 16, 1 => 0 + ), 1 => array( + 0 => 10, 1 => 1 + ), 2 => array( + 0 => 10, 1 => 1 + ), 3 => array( + 0 => 10, 1 => 1 + ), 4 => array( + 0 => 10, 1 => 1 + ) + ), 11 => array( + 0 => array( + 0 => 12, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 1 + ) + ), 12 => array( + 0 => array( + 0 => 0, 1 => 0 + ), 1 => array( + 0 => 11, 1 => -1 + ) + ), 13 => array( + 0 => array( + 0 => 14, 1 => 1 + ), 1 => array( + 0 => 14, 1 => 1 + ), 2 => array( + 0 => 15, 1 => 1 + ), 3 => array( + 0 => 13, 1 => 1 + ), 4 => array( + 0 => 13, 1 => 1 + ), 5 => array( + 0 => 16, 1 => -1 + ) + ), 14 => array( + 0 => array( + 0 => 14, 1 => 0 + ), 1 => array( + 0 => 14, 1 => 0 + ), 2 => array( + 0 => 16, 1 => -1 + ) + ), 15 => array( + 0 => array( + 0 => 16, 1 => -1 + ) + ), 17 => NULL + ); + $this->initialState = 0; + $this->returnState = 16; + $this->quitState = 17; + $this->flags = array( + 0 => 0, 1 => 4, 2 => 5, 3 => 4, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 0, 9 => 0, 10 => 4, 11 => 0, 12 => 0, 13 => 4, 14 => 0, 15 => 0, 17 => 8 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL, 11 => NULL, 12 => NULL, 13 => NULL, 14 => NULL, 15 => NULL, 17 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => 'xlang', 2 => NULL, 3 => 'php-comment', 4 => 'php-comment', 5 => 'php-var', 6 => 'php-var', 7 => 'php-quote', 8 => 'php-quote', 9 => 'php-quote', 10 => 'php-quote', 11 => 'php-quote', 12 => 'php-quote', 13 => 'php-num', 14 => 'php-num', 15 => 'php-num', 17 => 'xlang' + ); + $this->keywords = array( + 0 => 'php-keyword', 1 => array( + 'abstract' => 1, 'and' => 1, 'array' => 1, 'as' => 1, 'break' => 1, 'callable' => 1, 'case' => 1, 'catch' => 1, 'class' => 1, 'clone' => 1, 'const' => 1, 'continue' => 1, 'declare' => 1, 'default' => 1, 'do' => 1, 'else' => 1, 'elseif' => 1, 'enddeclare' => 1, 'endfor' => 1, 'endforeach' => 1, 'endif' => 1, 'endswitch' => 1, 'endwhile' => 2, 'extends' => 1, 'final' => 1, 'for' => 1, + 'foreach' => 1, 'function' => 1, 'global' => 1, 'goto' => 1, 'if' => 1, 'implements' => 1, 'interface' => 1, 'instanceof' => 1, 'insteadof' => 1, 'namespace' => 1, 'new' => 1, 'or' => 1, 'private' => 1, 'protected' => 1, 'public' => 1, 'static' => 1, 'switch' => 1, 'throw' => 1, 'trait' => 1, 'try' => 1, 'use' => 1, 'var' => 1, 'while' => 1, 'xor' => 1, '__CLASS__' => 1, '__DIR__' => 1, + '__FILE__' => 1, '__LINE__' => 1, '__FUNCTION__' => 1, '__METHOD__' => 1, '__NAMESPACE__' => 1, '__TRAIT__' => 1, 'die' => 1, 'echo' => 1, 'empty' => 1, 'exit' => 1, 'eval' => 2, 'include' => 1, 'include_once' => 1, 'isset' => 1, 'list' => 1, 'require' => 1, 'require_once' => 1, 'return' => 1, 'print' => 1, 'unset' => 1, 'true' => 1, 'false' => 1, 'null' => 1, 'abs' => 2, 'acos' => 2, 'acosh' => 2, + 'ada_afetch' => 2, 'ada_autocommit' => 2, 'ada_close' => 2, 'ada_commit' => 2, 'ada_connect' => 2, 'ada_exec' => 2, 'ada_fetchrow' => 2, 'ada_fieldname' => 2, 'ada_fieldnum' => 2, 'ada_fieldtype' => 2, 'ada_freeresult' => 2, 'ada_numfields' => 2, 'ada_numrows' => 2, 'ada_result' => 2, 'ada_resultall' => 2, 'ada_rollback' => 2, 'addcslashes' => 2, 'addslashes' => 2, 'aggregate' => 2, 'aggregate_methods' => 2, 'aggregate_methods_by_list' => 2, 'aggregate_methods_by_regexp' => 2, 'aggregate_properties' => 2, 'aggregate_properties_by_list' => 2, 'aggregate_properties_by_regexp' => 2, 'aggregation_info' => 2, + 'apache_child_terminate' => 2, 'apache_get_modules' => 2, 'apache_get_version' => 2, 'apache_getenv' => 2, 'apache_lookup_uri' => 2, 'apache_note' => 2, 'apache_request_headers' => 2, 'apache_response_headers' => 2, 'apache_setenv' => 2, 'apc_add' => 2, 'apc_bin_dump' => 2, 'apc_bin_dumpfile' => 2, 'apc_bin_load' => 2, 'apc_bin_loadfile' => 2, 'apc_cache_info' => 2, 'apc_cas' => 2, 'apc_clear_cache' => 2, 'apc_compile_file' => 2, 'apc_dec' => 2, 'apc_define_constants' => 2, 'apc_delete' => 2, 'apc_delete_file' => 2, 'apc_exists' => 2, 'apc_fetch' => 2, 'apc_inc' => 2, 'apc_load_constants' => 2, + 'apc_sma_info' => 2, 'apc_store' => 2, 'array_change_key_case' => 2, 'array_chunk' => 2, 'array_combine' => 2, 'array_count_values' => 2, 'array_diff' => 2, 'array_diff_assoc' => 2, 'array_diff_key' => 2, 'array_diff_uassoc' => 2, 'array_diff_ukey' => 2, 'array_fill' => 2, 'array_fill_keys' => 2, 'array_filter' => 2, 'array_flip' => 2, 'array_intersect' => 2, 'array_intersect_assoc' => 2, 'array_intersect_key' => 2, 'array_intersect_uassoc' => 2, 'array_intersect_ukey' => 2, 'array_key_exists' => 2, 'array_keys' => 2, 'array_map' => 2, 'array_merge' => 2, 'array_merge_recursive' => 2, 'array_multisort' => 2, + 'array_pad' => 2, 'array_pop' => 2, 'array_product' => 2, 'array_push' => 2, 'array_rand' => 2, 'array_reduce' => 2, 'array_replace' => 2, 'array_replace_recursive' => 2, 'array_reverse' => 2, 'array_search' => 2, 'array_shift' => 2, 'array_slice' => 2, 'array_splice' => 2, 'array_sum' => 2, 'array_udiff' => 2, 'array_udiff_assoc' => 2, 'array_udiff_uassoc' => 2, 'array_uintersect' => 2, 'array_uintersect_assoc' => 2, 'array_uintersect_uassoc' => 2, 'array_unique' => 2, 'array_unshift' => 2, 'array_values' => 2, 'array_walk' => 2, 'array_walk_recursive' => 2, 'arsort' => 2, + 'asin' => 2, 'asinh' => 2, 'asort' => 2, 'aspell_check' => 2, 'aspell_check-raw' => 2, 'aspell_new' => 2, 'aspell_suggest' => 2, 'assert' => 2, 'assert_options' => 2, 'atan' => 2, 'atan2' => 2, 'atanh' => 2, 'base64_decode' => 2, 'base64_encode' => 2, 'base_convert' => 2, 'basename' => 2, 'bcadd' => 2, 'bccomp' => 2, 'bcdiv' => 2, 'bcmod' => 2, 'bcmul' => 2, 'bcompiler_load' => 2, 'bcompiler_load_exe' => 2, 'bcompiler_parse_class' => 2, 'bcompiler_read' => 2, 'bcompiler_write_class' => 2, + 'bcompiler_write_constant' => 2, 'bcompiler_write_exe_footer' => 2, 'bcompiler_write_file' => 2, 'bcompiler_write_footer' => 2, 'bcompiler_write_function' => 2, 'bcompiler_write_functions_from_file' => 2, 'bcompiler_write_header' => 2, 'bcompiler_write_included_filename' => 2, 'bcpow' => 2, 'bcpowmod' => 2, 'bcscale' => 2, 'bcsqrt' => 2, 'bcsub' => 2, 'bin2hex' => 2, 'bind_textdomain_codeset' => 2, 'bindec' => 2, 'bindtextdomain' => 2, 'bitset_empty' => 2, 'bitset_equal' => 2, 'bitset_excl' => 2, 'bitset_fill' => 2, 'bitset_from_array' => 2, 'bitset_from_hash' => 2, 'bitset_from_string' => 2, 'bitset_in' => 2, 'bitset_incl' => 2, + 'bitset_intersection' => 2, 'bitset_invert' => 2, 'bitset_is_empty' => 2, 'bitset_subset' => 2, 'bitset_to_array' => 2, 'bitset_to_hash' => 2, 'bitset_to_string' => 2, 'bitset_union' => 2, 'blenc_encrypt' => 2, 'bzclose' => 2, 'bzcompress' => 2, 'bzdecompress' => 2, 'bzerrno' => 2, 'bzerror' => 2, 'bzerrstr' => 2, 'bzflush' => 2, 'bzopen' => 2, 'bzread' => 2, 'bzwrite' => 2, 'cal_days_in_month' => 2, 'cal_from_jd' => 2, 'cal_info' => 2, 'cal_to_jd' => 2, 'call_user_func' => 2, 'call_user_func_array' => 2, 'call_user_method' => 2, + 'call_user_method_array' => 2, 'ceil' => 2, 'chdir' => 2, 'checkdate' => 2, 'checkdnsrr' => 2, 'chgrp' => 2, 'chmod' => 2, 'chop' => 2, 'chown' => 2, 'chr' => 2, 'chunk_split' => 2, 'class_alias' => 2, 'class_exists' => 2, 'class_implements' => 2, 'class_parents' => 2, 'classkit_aggregate_methods' => 2, 'classkit_doc_comments' => 2, 'classkit_import' => 2, 'classkit_method_add' => 2, 'classkit_method_copy' => 2, 'classkit_method_redefine' => 2, 'classkit_method_remove' => 2, 'classkit_method_rename' => 2, 'clearstatcache' => 2, 'closedir' => 2, 'closelog' => 2, + 'collator_asort' => 2, 'collator_compare' => 2, 'collator_create' => 2, 'collator_get_attribute' => 2, 'collator_get_error_code' => 2, 'collator_get_error_message' => 2, 'collator_get_locale' => 2, 'collator_get_sort_key' => 2, 'collator_get_strength' => 2, 'collator_set_attribute' => 2, 'collator_set_strength' => 2, 'collator_sort' => 2, 'collator_sort_with_sort_keys' => 2, 'com_create_guid' => 2, 'com_event_sink' => 2, 'com_get_active_object' => 2, 'com_load_typelib' => 2, 'com_message_pump' => 2, 'com_print_typeinfo' => 2, 'compact' => 2, 'confirm_phpdoc_compiled' => 2, 'connection_aborted' => 2, 'connection_status' => 2, 'connection_timeout' => 2, 'constant' => 2, 'contained' => 2, + 'convert_cyr_string' => 2, 'convert_uudecode' => 2, 'convert_uuencode' => 2, 'copy' => 2, 'cos' => 2, 'cosh' => 2, 'count' => 2, 'count_chars' => 2, 'cpdf_add_annotation' => 2, 'cpdf_add_outline' => 2, 'cpdf_arc' => 2, 'cpdf_begin_text' => 2, 'cpdf_circle' => 2, 'cpdf_clip' => 2, 'cpdf_close' => 2, 'cpdf_closepath' => 2, 'cpdf_closepath_fill_stroke' => 2, 'cpdf_closepath_stroke' => 2, 'cpdf_continue_text' => 2, 'cpdf_curveto' => 2, 'cpdf_end_text' => 2, 'cpdf_fill' => 2, 'cpdf_fill_stroke' => 2, 'cpdf_finalize' => 2, 'cpdf_finalize_page' => 2, 'cpdf_global_set_document_limits' => 2, + 'cpdf_import_jpeg' => 2, 'cpdf_lineto' => 2, 'cpdf_moveto' => 2, 'cpdf_newpath' => 2, 'cpdf_open' => 2, 'cpdf_output_buffer' => 2, 'cpdf_page_init' => 2, 'cpdf_place_inline_image' => 2, 'cpdf_rect' => 2, 'cpdf_restore' => 2, 'cpdf_rlineto' => 2, 'cpdf_rmoveto' => 2, 'cpdf_rotate' => 2, 'cpdf_rotate_text' => 2, 'cpdf_save' => 2, 'cpdf_save_to_file' => 2, 'cpdf_scale' => 2, 'cpdf_set_action_url' => 2, 'cpdf_set_char_spacing' => 2, 'cpdf_set_creator' => 2, 'cpdf_set_current_page' => 2, 'cpdf_set_font' => 2, 'cpdf_set_font_directories' => 2, 'cpdf_set_font_map_file' => 2, 'cpdf_set_horiz_scaling' => 2, 'cpdf_set_keywords' => 2, + 'cpdf_set_leading' => 2, 'cpdf_set_page_animation' => 2, 'cpdf_set_subject' => 2, 'cpdf_set_text_matrix' => 2, 'cpdf_set_text_pos' => 2, 'cpdf_set_text_rendering' => 2, 'cpdf_set_text_rise' => 2, 'cpdf_set_title' => 2, 'cpdf_set_viewer_preferences' => 2, 'cpdf_set_word_spacing' => 2, 'cpdf_setdash' => 2, 'cpdf_setflat' => 2, 'cpdf_setgray' => 2, 'cpdf_setgray_fill' => 2, 'cpdf_setgray_stroke' => 2, 'cpdf_setlinecap' => 2, 'cpdf_setlinejoin' => 2, 'cpdf_setlinewidth' => 2, 'cpdf_setmiterlimit' => 2, 'cpdf_setrgbcolor' => 2, 'cpdf_setrgbcolor_fill' => 2, 'cpdf_setrgbcolor_stroke' => 2, 'cpdf_show' => 2, 'cpdf_show_xy' => 2, 'cpdf_stringwidth' => 2, 'cpdf_stroke' => 2, + 'cpdf_text' => 2, 'cpdf_translate' => 2, 'crack_check' => 2, 'crack_closedict' => 2, 'crack_getlastmessage' => 2, 'crack_opendict' => 2, 'crc32' => 2, 'create_function' => 2, 'crypt' => 2, 'ctype_alnum' => 2, 'ctype_alpha' => 2, 'ctype_cntrl' => 2, 'ctype_digit' => 2, 'ctype_graph' => 2, 'ctype_lower' => 2, 'ctype_print' => 2, 'ctype_punct' => 2, 'ctype_space' => 2, 'ctype_upper' => 2, 'ctype_xdigit' => 2, 'curl_close' => 2, 'curl_copy_handle' => 2, 'curl_errno' => 2, 'curl_error' => 2, 'curl_exec' => 2, 'curl_getinfo' => 2, + 'curl_init' => 2, 'curl_multi_add_handle' => 2, 'curl_multi_close' => 2, 'curl_multi_exec' => 2, 'curl_multi_getcontent' => 2, 'curl_multi_info_read' => 2, 'curl_multi_init' => 2, 'curl_multi_remove_handle' => 2, 'curl_multi_select' => 2, 'curl_setopt' => 2, 'curl_setopt_array' => 2, 'curl_version' => 2, 'current' => 2, 'cvsclient_connect' => 2, 'cvsclient_log' => 2, 'cvsclient_login' => 2, 'cvsclient_retrieve' => 2, 'date' => 2, 'date_add' => 2, 'date_create' => 2, 'date_create_from_format' => 2, 'date_date_set' => 2, 'date_default_timezone_get' => 2, 'date_default_timezone_set' => 2, 'date_diff' => 2, 'date_format' => 2, + 'date_get_last_errors' => 2, 'date_interval_create_from_date_string' => 2, 'date_interval_format' => 2, 'date_isodate_set' => 2, 'date_modify' => 2, 'date_offset_get' => 2, 'date_parse' => 2, 'date_parse_from_format' => 2, 'date_sub' => 2, 'date_sun_info' => 2, 'date_sunrise' => 2, 'date_sunset' => 2, 'date_time_set' => 2, 'date_timestamp_get' => 2, 'date_timestamp_set' => 2, 'date_timezone_get' => 2, 'date_timezone_set' => 2, 'datefmt_create' => 2, 'datefmt_format' => 2, 'datefmt_get_calendar' => 2, 'datefmt_get_datetype' => 2, 'datefmt_get_error_code' => 2, 'datefmt_get_error_message' => 2, 'datefmt_get_locale' => 2, 'datefmt_get_pattern' => 2, 'datefmt_get_timetype' => 2, + 'datefmt_get_timezone_id' => 2, 'datefmt_is_lenient' => 2, 'datefmt_localtime' => 2, 'datefmt_parse' => 2, 'datefmt_set_calendar' => 2, 'datefmt_set_lenient' => 2, 'datefmt_set_pattern' => 2, 'datefmt_set_timezone_id' => 2, 'db_id_list' => 2, 'dba_close' => 2, 'dba_delete' => 2, 'dba_exists' => 2, 'dba_fetch' => 2, 'dba_firstkey' => 2, 'dba_handlers' => 2, 'dba_insert' => 2, 'dba_key_split' => 2, 'dba_list' => 2, 'dba_nextkey' => 2, 'dba_open' => 2, 'dba_optimize' => 2, 'dba_popen' => 2, 'dba_replace' => 2, 'dba_sync' => 2, 'dbase_add_record' => 2, 'dbase_close' => 2, + 'dbase_create' => 2, 'dbase_delete_record' => 2, 'dbase_get_header_info' => 2, 'dbase_get_record' => 2, 'dbase_get_record_with_names' => 2, 'dbase_numfields' => 2, 'dbase_numrecords' => 2, 'dbase_open' => 2, 'dbase_pack' => 2, 'dbase_replace_record' => 2, 'dbg_get_all_contexts' => 2, 'dbg_get_all_module_names' => 2, 'dbg_get_all_source_lines' => 2, 'dbg_get_context_name' => 2, 'dbg_get_loaded_zendextensions' => 2, 'dbg_get_module_name' => 2, 'dbg_get_profiler_results' => 2, 'dbg_get_source_context' => 2, 'dblist' => 2, 'dbmclose' => 2, 'dbmdelete' => 2, 'dbmexists' => 2, 'dbmfetch' => 2, 'dbmfirstkey' => 2, 'dbminsert' => 2, 'dbmnextkey' => 2, + 'dbmopen' => 2, 'dbmreplace' => 2, 'dbx_close' => 2, 'dbx_compare' => 2, 'dbx_connect' => 2, 'dbx_error' => 2, 'dbx_escape_string' => 2, 'dbx_fetch_row' => 2, 'dbx_query' => 2, 'dbx_sort' => 2, 'dcgettext' => 2, 'dcngettext' => 2, 'deaggregate' => 2, 'debug_backtrace' => 2, 'debug_print_backtrace' => 2, 'debug_zval_dump' => 2, 'debugbreak' => 2, 'debugger_off' => 2, 'debugger_on' => 2, 'decbin' => 2, 'dechex' => 2, 'decoct' => 2, 'define' => 2, 'define_syslog_variables' => 2, 'defined' => 2, 'deg2rad' => 2, + 'delete' => 2, 'dgettext' => 2, 'dio_close' => 2, 'dio_open' => 2, 'dio_read' => 2, 'dio_seek' => 2, 'dio_stat' => 2, 'dio_write' => 2, 'dir' => 2, 'dirname' => 2, 'disk_free_space' => 2, 'disk_total_space' => 2, 'diskfreespace' => 2, 'dl' => 2, 'dngettext' => 2, 'dns_check_record' => 2, 'dns_get_mx' => 2, 'dns_get_record' => 2, 'docblock_token_name' => 2, 'docblock_tokenize' => 2, 'dom_import_simplexml' => 2, 'domxml_add_root' => 2, 'domxml_attributes' => 2, 'domxml_children' => 2, 'domxml_doc_add_root' => 2, 'domxml_doc_document_element' => 2, + 'domxml_doc_get_element_by_id' => 2, 'domxml_doc_get_elements_by_tagname' => 2, 'domxml_doc_get_root' => 2, 'domxml_doc_set_root' => 2, 'domxml_doc_validate' => 2, 'domxml_doc_xinclude' => 2, 'domxml_dump_mem' => 2, 'domxml_dump_mem_file' => 2, 'domxml_dump_node' => 2, 'domxml_dumpmem' => 2, 'domxml_elem_get_attribute' => 2, 'domxml_elem_set_attribute' => 2, 'domxml_get_attribute' => 2, 'domxml_getattr' => 2, 'domxml_html_dump_mem' => 2, 'domxml_new_child' => 2, 'domxml_new_doc' => 2, 'domxml_new_xmldoc' => 2, 'domxml_node' => 2, 'domxml_node_add_namespace' => 2, 'domxml_node_attributes' => 2, 'domxml_node_children' => 2, 'domxml_node_get_content' => 2, 'domxml_node_has_attributes' => 2, 'domxml_node_new_child' => 2, 'domxml_node_set_content' => 2, + 'domxml_node_set_namespace' => 2, 'domxml_node_unlink_node' => 2, 'domxml_open_file' => 2, 'domxml_open_mem' => 2, 'domxml_parser' => 2, 'domxml_parser_add_chunk' => 2, 'domxml_parser_cdata_section' => 2, 'domxml_parser_characters' => 2, 'domxml_parser_comment' => 2, 'domxml_parser_end' => 2, 'domxml_parser_end_document' => 2, 'domxml_parser_end_element' => 2, 'domxml_parser_entity_reference' => 2, 'domxml_parser_get_document' => 2, 'domxml_parser_namespace_decl' => 2, 'domxml_parser_processing_instruction' => 2, 'domxml_parser_start_document' => 2, 'domxml_parser_start_element' => 2, 'domxml_root' => 2, 'domxml_set_attribute' => 2, 'domxml_setattr' => 2, 'domxml_substitute_entities_default' => 2, 'domxml_unlink_node' => 2, 'domxml_version' => 2, 'domxml_xmltree' => 2, 'doubleval' => 2, + 'each' => 2, 'easter_date' => 2, 'easter_days' => 2, 'end' => 2, 'ereg' => 2, 'ereg_replace' => 2, 'eregi' => 2, 'eregi_replace' => 2, 'error_get_last' => 2, 'error_log' => 2, 'error_reporting' => 2, 'escapeshellarg' => 2, 'escapeshellcmd' => 2, 'event_deschedule' => 2, 'event_dispatch' => 2, 'event_free' => 2, 'event_handle_signal' => 2, 'event_have_events' => 2, 'event_init' => 2, 'event_new' => 2, 'event_pending' => 2, 'event_priority_set' => 2, 'event_schedule' => 2, 'event_set' => 2, 'event_timeout' => 2, 'exec' => 2, + 'exif_imagetype' => 2, 'exif_read_data' => 2, 'exif_tagname' => 2, 'exif_thumbnail' => 2, 'exp' => 2, 'explode' => 2, 'expm1' => 2, 'extension_loaded' => 2, 'extract' => 2, 'ezmlm_hash' => 2, 'fbird_add_user' => 2, 'fbird_affected_rows' => 2, 'fbird_backup' => 2, 'fbird_blob_add' => 2, 'fbird_blob_cancel' => 2, 'fbird_blob_close' => 2, 'fbird_blob_create' => 2, 'fbird_blob_echo' => 2, 'fbird_blob_get' => 2, 'fbird_blob_import' => 2, 'fbird_blob_info' => 2, 'fbird_blob_open' => 2, 'fbird_close' => 2, 'fbird_commit' => 2, 'fbird_commit_ret' => 2, 'fbird_connect' => 2, + 'fbird_db_info' => 2, 'fbird_delete_user' => 2, 'fbird_drop_db' => 2, 'fbird_errcode' => 2, 'fbird_errmsg' => 2, 'fbird_execute' => 2, 'fbird_fetch_assoc' => 2, 'fbird_fetch_object' => 2, 'fbird_fetch_row' => 2, 'fbird_field_info' => 2, 'fbird_free_event_handler' => 2, 'fbird_free_query' => 2, 'fbird_free_result' => 2, 'fbird_gen_id' => 2, 'fbird_maintain_db' => 2, 'fbird_modify_user' => 2, 'fbird_name_result' => 2, 'fbird_num_fields' => 2, 'fbird_num_params' => 2, 'fbird_param_info' => 2, 'fbird_pconnect' => 2, 'fbird_prepare' => 2, 'fbird_query' => 2, 'fbird_restore' => 2, 'fbird_rollback' => 2, 'fbird_rollback_ret' => 2, + 'fbird_server_info' => 2, 'fbird_service_attach' => 2, 'fbird_service_detach' => 2, 'fbird_set_event_handler' => 2, 'fbird_trans' => 2, 'fbird_wait_event' => 2, 'fclose' => 2, 'fdf_add_doc_javascript' => 2, 'fdf_add_template' => 2, 'fdf_close' => 2, 'fdf_create' => 2, 'fdf_enum_values' => 2, 'fdf_errno' => 2, 'fdf_error' => 2, 'fdf_get_ap' => 2, 'fdf_get_attachment' => 2, 'fdf_get_encoding' => 2, 'fdf_get_file' => 2, 'fdf_get_flags' => 2, 'fdf_get_opt' => 2, 'fdf_get_status' => 2, 'fdf_get_value' => 2, 'fdf_get_version' => 2, 'fdf_header' => 2, 'fdf_next_field_name' => 2, 'fdf_open' => 2, + 'fdf_open_string' => 2, 'fdf_remove_item' => 2, 'fdf_save' => 2, 'fdf_save_string' => 2, 'fdf_set_ap' => 2, 'fdf_set_encoding' => 2, 'fdf_set_file' => 2, 'fdf_set_flags' => 2, 'fdf_set_javascript_action' => 2, 'fdf_set_on_import_javascript' => 2, 'fdf_set_opt' => 2, 'fdf_set_status' => 2, 'fdf_set_submit_form_action' => 2, 'fdf_set_target_frame' => 2, 'fdf_set_value' => 2, 'fdf_set_version' => 2, 'feof' => 2, 'fflush' => 2, 'fgetc' => 2, 'fgetcsv' => 2, 'fgets' => 2, 'fgetss' => 2, 'file' => 2, 'file_exists' => 2, 'file_get_contents' => 2, 'file_put_contents' => 2, + 'fileatime' => 2, 'filectime' => 2, 'filegroup' => 2, 'fileinode' => 2, 'filemtime' => 2, 'fileowner' => 2, 'fileperms' => 2, 'filepro' => 2, 'filepro_fieldcount' => 2, 'filepro_fieldname' => 2, 'filepro_fieldtype' => 2, 'filepro_fieldwidth' => 2, 'filepro_retrieve' => 2, 'filepro_rowcount' => 2, 'filesize' => 2, 'filetype' => 2, 'filter_has_var' => 2, 'filter_id' => 2, 'filter_input' => 2, 'filter_input_array' => 2, 'filter_list' => 2, 'filter_var' => 2, 'filter_var_array' => 2, 'finfo_buffer' => 2, 'finfo_close' => 2, 'finfo_file' => 2, + 'finfo_open' => 2, 'finfo_set_flags' => 2, 'floatval' => 2, 'flock' => 2, 'floor' => 2, 'flush' => 2, 'fmod' => 2, 'fnmatch' => 2, 'fopen' => 2, 'forward_static_call' => 2, 'forward_static_call_array' => 2, 'fpassthru' => 2, 'fprintf' => 2, 'fputcsv' => 2, 'fputs' => 2, 'fread' => 2, 'frenchtojd' => 2, 'fribidi_charset_info' => 2, 'fribidi_get_charsets' => 2, 'fribidi_log2vis' => 2, 'fscanf' => 2, 'fseek' => 2, 'fsockopen' => 2, 'fstat' => 2, 'ftell' => 2, 'ftok' => 2, + 'ftp_alloc' => 2, 'ftp_cdup' => 2, 'ftp_chdir' => 2, 'ftp_chmod' => 2, 'ftp_close' => 2, 'ftp_connect' => 2, 'ftp_delete' => 2, 'ftp_exec' => 2, 'ftp_fget' => 2, 'ftp_fput' => 2, 'ftp_get' => 2, 'ftp_get_option' => 2, 'ftp_login' => 2, 'ftp_mdtm' => 2, 'ftp_mkdir' => 2, 'ftp_nb_continue' => 2, 'ftp_nb_fget' => 2, 'ftp_nb_fput' => 2, 'ftp_nb_get' => 2, 'ftp_nb_put' => 2, 'ftp_nlist' => 2, 'ftp_pasv' => 2, 'ftp_put' => 2, 'ftp_pwd' => 2, 'ftp_quit' => 2, 'ftp_raw' => 2, + 'ftp_rawlist' => 2, 'ftp_rename' => 2, 'ftp_rmdir' => 2, 'ftp_set_option' => 2, 'ftp_site' => 2, 'ftp_size' => 2, 'ftp_ssl_connect' => 2, 'ftp_systype' => 2, 'ftruncate' => 2, 'func_get_arg' => 2, 'func_get_args' => 2, 'func_num_args' => 2, 'function_exists' => 2, 'fwrite' => 2, 'gc_collect_cycles' => 2, 'gc_disable' => 2, 'gc_enable' => 2, 'gc_enabled' => 2, 'gd_info' => 2, 'get_browser' => 2, 'get_called_class' => 2, 'get_cfg_var' => 2, 'get_class' => 2, 'get_class_methods' => 2, 'get_class_vars' => 2, 'get_current_user' => 2, + 'get_declared_classes' => 2, 'get_declared_interfaces' => 2, 'get_declared_traits' => 2, 'get_defined_constants' => 2, 'get_defined_functions' => 2, 'get_defined_vars' => 2, 'get_extension_funcs' => 2, 'get_headers' => 2, 'get_html_translation_table' => 2, 'get_include_path' => 2, 'get_included_files' => 2, 'get_loaded_extensions' => 2, 'get_magic_quotes_gpc' => 2, 'get_magic_quotes_runtime' => 2, 'get_meta_tags' => 2, 'get_object_vars' => 2, 'get_parent_class' => 2, 'get_required_files' => 2, 'get_resource_type' => 2, 'getallheaders' => 2, 'getcwd' => 2, 'getdate' => 2, 'getenv' => 2, 'gethostbyaddr' => 2, 'gethostbyname' => 2, 'gethostbynamel' => 2, + 'gethostname' => 2, 'getimagesize' => 2, 'getimagesizefromstring' => 2, 'getlastmod' => 2, 'getmxrr' => 2, 'getmygid' => 2, 'getmyinode' => 2, 'getmypid' => 2, 'getmyuid' => 2, 'getopt' => 2, 'getprotobyname' => 2, 'getprotobynumber' => 2, 'getrandmax' => 2, 'getrusage' => 2, 'getservbyname' => 2, 'getservbyport' => 2, 'gettext' => 2, 'gettimeofday' => 2, 'gettype' => 2, 'glob' => 2, 'gmdate' => 2, 'gmmktime' => 2, 'gmp_abs' => 2, 'gmp_add' => 2, 'gmp_and' => 2, 'gmp_clrbit' => 2, + 'gmp_cmp' => 2, 'gmp_com' => 2, 'gmp_div' => 2, 'gmp_div_q' => 2, 'gmp_div_qr' => 2, 'gmp_div_r' => 2, 'gmp_divexact' => 2, 'gmp_fact' => 2, 'gmp_gcd' => 2, 'gmp_gcdext' => 2, 'gmp_hamdist' => 2, 'gmp_init' => 2, 'gmp_intval' => 2, 'gmp_invert' => 2, 'gmp_jacobi' => 2, 'gmp_legendre' => 2, 'gmp_mod' => 2, 'gmp_mul' => 2, 'gmp_neg' => 2, 'gmp_nextprime' => 2, 'gmp_or' => 2, 'gmp_perfect_square' => 2, 'gmp_popcount' => 2, 'gmp_pow' => 2, 'gmp_powm' => 2, 'gmp_prob_prime' => 2, + 'gmp_random' => 2, 'gmp_scan0' => 2, 'gmp_scan1' => 2, 'gmp_setbit' => 2, 'gmp_sign' => 2, 'gmp_sqrt' => 2, 'gmp_sqrtrem' => 2, 'gmp_strval' => 2, 'gmp_sub' => 2, 'gmp_xor' => 2, 'gmstrftime' => 2, 'gopher_parsedir' => 2, 'grapheme_extract' => 2, 'grapheme_stripos' => 2, 'grapheme_stristr' => 2, 'grapheme_strlen' => 2, 'grapheme_strpos' => 2, 'grapheme_strripos' => 2, 'grapheme_strrpos' => 2, 'grapheme_strstr' => 2, 'grapheme_substr' => 2, 'gregoriantojd' => 2, 'gzclose' => 2, 'gzcompress' => 2, 'gzdecode' => 2, 'gzdeflate' => 2, + 'gzencode' => 2, 'gzeof' => 2, 'gzfile' => 2, 'gzgetc' => 2, 'gzgets' => 2, 'gzgetss' => 2, 'gzinflate' => 2, 'gzopen' => 2, 'gzpassthru' => 2, 'gzputs' => 2, 'gzread' => 2, 'gzrewind' => 2, 'gzseek' => 2, 'gztell' => 2, 'gzuncompress' => 2, 'gzwrite' => 2, 'hash' => 2, 'hash_algos' => 2, 'hash_copy' => 2, 'hash_file' => 2, 'hash_final' => 2, 'hash_hmac' => 2, 'hash_hmac_file' => 2, 'hash_init' => 2, 'hash_update' => 2, 'hash_update_file' => 2, + 'hash_update_stream' => 2, 'header' => 2, 'header_register_callback' => 2, 'header_remove' => 2, 'headers_list' => 2, 'headers_sent' => 2, 'hebrev' => 2, 'hebrevc' => 2, 'hex2bin' => 2, 'hexdec' => 2, 'highlight_file' => 2, 'highlight_string' => 2, 'html_doc' => 2, 'html_doc_file' => 2, 'html_entity_decode' => 2, 'htmlentities' => 2, 'htmlspecialchars' => 2, 'htmlspecialchars_decode' => 2, 'http_build_cookie' => 2, 'http_build_query' => 2, 'http_build_str' => 2, 'http_build_url' => 2, 'http_cache_etag' => 2, 'http_cache_last_modified' => 2, 'http_chunked_decode' => 2, 'http_date' => 2, + 'http_deflate' => 2, 'http_get' => 2, 'http_get_request_body' => 2, 'http_get_request_body_stream' => 2, 'http_get_request_headers' => 2, 'http_head' => 2, 'http_inflate' => 2, 'http_match_etag' => 2, 'http_match_modified' => 2, 'http_match_request_header' => 2, 'http_negotiate' => 2, 'http_negotiate_charset' => 2, 'http_negotiate_content_type' => 2, 'http_negotiate_language' => 2, 'http_parse_cookie' => 2, 'http_parse_headers' => 2, 'http_parse_message' => 2, 'http_parse_params' => 2, 'http_persistent_handles_clean' => 2, 'http_persistent_handles_count' => 2, 'http_persistent_handles_ident' => 2, 'http_post_data' => 2, 'http_post_fields' => 2, 'http_put_data' => 2, 'http_put_file' => 2, 'http_put_stream' => 2, + 'http_redirect' => 2, 'http_request' => 2, 'http_request_body_encode' => 2, 'http_request_method_exists' => 2, 'http_request_method_name' => 2, 'http_request_method_register' => 2, 'http_request_method_unregister' => 2, 'http_response_code' => 2, 'http_send_content_disposition' => 2, 'http_send_content_type' => 2, 'http_send_data' => 2, 'http_send_file' => 2, 'http_send_last_modified' => 2, 'http_send_status' => 2, 'http_send_stream' => 2, 'http_support' => 2, 'http_throttle' => 2, 'hw_array2objrec' => 2, 'hw_children' => 2, 'hw_childrenobj' => 2, 'hw_close' => 2, 'hw_connect' => 2, 'hw_cp' => 2, 'hw_deleteobject' => 2, 'hw_docbyanchor' => 2, 'hw_docbyanchorobj' => 2, + 'hw_documentattributes' => 2, 'hw_documentbodytag' => 2, 'hw_documentcontent' => 2, 'hw_documentsetcontent' => 2, 'hw_documentsize' => 2, 'hw_edittext' => 2, 'hw_error' => 2, 'hw_errormsg' => 2, 'hw_free_document' => 2, 'hw_getanchors' => 2, 'hw_getanchorsobj' => 2, 'hw_getandlock' => 2, 'hw_getchildcoll' => 2, 'hw_getchildcollobj' => 2, 'hw_getchilddoccoll' => 2, 'hw_getchilddoccollobj' => 2, 'hw_getobject' => 2, 'hw_getobjectbyquery' => 2, 'hw_getobjectbyquerycoll' => 2, 'hw_getobjectbyquerycollobj' => 2, 'hw_getobjectbyqueryobj' => 2, 'hw_getparents' => 2, 'hw_getparentsobj' => 2, 'hw_getremote' => 2, 'hw_getremotechildren' => 2, 'hw_getsrcbydestobj' => 2, + 'hw_gettext' => 2, 'hw_identify' => 2, 'hw_incollections' => 2, 'hw_info' => 2, 'hw_inscoll' => 2, 'hw_insdoc' => 2, 'hw_insertdocument' => 2, 'hw_insertobject' => 2, 'hw_mapid' => 2, 'hw_modifyobject' => 2, 'hw_mv' => 2, 'hw_new_document' => 2, 'hw_objrec2array' => 2, 'hw_outputdocument' => 2, 'hw_pconnect' => 2, 'hw_pipedocument' => 2, 'hw_root' => 2, 'hw_unlock' => 2, 'hw_username' => 2, 'hw_who' => 2, 'hypot' => 2, 'i18n_convert' => 2, 'i18n_discover_encoding' => 2, 'i18n_http_input' => 2, 'i18n_http_output' => 2, 'i18n_internal_encoding' => 2, + 'i18n_ja_jp_hantozen' => 2, 'i18n_mime_header_decode' => 2, 'i18n_mime_header_encode' => 2, 'ibase_add_user' => 2, 'ibase_affected_rows' => 2, 'ibase_backup' => 2, 'ibase_bind' => 2, 'ibase_blob_add' => 2, 'ibase_blob_cancel' => 2, 'ibase_blob_close' => 2, 'ibase_blob_create' => 2, 'ibase_blob_echo' => 2, 'ibase_blob_get' => 2, 'ibase_blob_import' => 2, 'ibase_blob_info' => 2, 'ibase_blob_open' => 2, 'ibase_close' => 2, 'ibase_commit' => 2, 'ibase_commit_ret' => 2, 'ibase_connect' => 2, 'ibase_db_info' => 2, 'ibase_delete_user' => 2, 'ibase_drop_db' => 2, 'ibase_errcode' => 2, 'ibase_errmsg' => 2, 'ibase_execute' => 2, + 'ibase_fetch_assoc' => 2, 'ibase_fetch_object' => 2, 'ibase_fetch_row' => 2, 'ibase_field_info' => 2, 'ibase_free_event_handler' => 2, 'ibase_free_query' => 2, 'ibase_free_result' => 2, 'ibase_gen_id' => 2, 'ibase_maintain_db' => 2, 'ibase_modify_user' => 2, 'ibase_name_result' => 2, 'ibase_num_fields' => 2, 'ibase_num_params' => 2, 'ibase_param_info' => 2, 'ibase_pconnect' => 2, 'ibase_prepare' => 2, 'ibase_query' => 2, 'ibase_restore' => 2, 'ibase_rollback' => 2, 'ibase_rollback_ret' => 2, 'ibase_server_info' => 2, 'ibase_service_attach' => 2, 'ibase_service_detach' => 2, 'ibase_set_event_handler' => 2, 'ibase_timefmt' => 2, 'ibase_trans' => 2, + 'ibase_wait_event' => 2, 'iconv' => 2, 'iconv_get_encoding' => 2, 'iconv_mime_decode' => 2, 'iconv_mime_decode_headers' => 2, 'iconv_mime_encode' => 2, 'iconv_set_encoding' => 2, 'iconv_strlen' => 2, 'iconv_strpos' => 2, 'iconv_strrpos' => 2, 'iconv_substr' => 2, 'id3_get_frame_long_name' => 2, 'id3_get_frame_short_name' => 2, 'id3_get_genre_id' => 2, 'id3_get_genre_list' => 2, 'id3_get_genre_name' => 2, 'id3_get_tag' => 2, 'id3_get_version' => 2, 'id3_remove_tag' => 2, 'id3_set_tag' => 2, 'idate' => 2, 'idn_to_ascii' => 2, 'idn_to_utf8' => 2, 'ifx_affected_rows' => 2, 'ifx_blobinfile_mode' => 2, 'ifx_byteasvarchar' => 2, + 'ifx_close' => 2, 'ifx_connect' => 2, 'ifx_copy_blob' => 2, 'ifx_create_blob' => 2, 'ifx_create_char' => 2, 'ifx_do' => 2, 'ifx_error' => 2, 'ifx_errormsg' => 2, 'ifx_fetch_row' => 2, 'ifx_fieldproperties' => 2, 'ifx_fieldtypes' => 2, 'ifx_free_blob' => 2, 'ifx_free_char' => 2, 'ifx_free_result' => 2, 'ifx_free_slob' => 2, 'ifx_get_blob' => 2, 'ifx_get_char' => 2, 'ifx_getsqlca' => 2, 'ifx_htmltbl_result' => 2, 'ifx_nullformat' => 2, 'ifx_num_fields' => 2, 'ifx_num_rows' => 2, 'ifx_pconnect' => 2, 'ifx_prepare' => 2, 'ifx_query' => 2, 'ifx_textasvarchar' => 2, + 'ifx_update_blob' => 2, 'ifx_update_char' => 2, 'ifxus_close_slob' => 2, 'ifxus_create_slob' => 2, 'ifxus_open_slob' => 2, 'ifxus_read_slob' => 2, 'ifxus_seek_slob' => 2, 'ifxus_tell_slob' => 2, 'ifxus_write_slob' => 2, 'ignore_user_abort' => 2, 'image2wbmp' => 2, 'image_type_to_extension' => 2, 'image_type_to_mime_type' => 2, 'imagealphablending' => 2, 'imageantialias' => 2, 'imagearc' => 2, 'imagechar' => 2, 'imagecharup' => 2, 'imagecolorallocate' => 2, 'imagecolorallocatealpha' => 2, 'imagecolorat' => 2, 'imagecolorclosest' => 2, 'imagecolorclosestalpha' => 2, 'imagecolorclosesthwb' => 2, 'imagecolordeallocate' => 2, 'imagecolorexact' => 2, + 'imagecolorexactalpha' => 2, 'imagecolormatch' => 2, 'imagecolorresolve' => 2, 'imagecolorresolvealpha' => 2, 'imagecolorset' => 2, 'imagecolorsforindex' => 2, 'imagecolorstotal' => 2, 'imagecolortransparent' => 2, 'imageconvolution' => 2, 'imagecopy' => 2, 'imagecopymerge' => 2, 'imagecopymergegray' => 2, 'imagecopyresampled' => 2, 'imagecopyresized' => 2, 'imagecreate' => 2, 'imagecreatefromgd' => 2, 'imagecreatefromgd2' => 2, 'imagecreatefromgd2part' => 2, 'imagecreatefromgif' => 2, 'imagecreatefromjpeg' => 2, 'imagecreatefrompng' => 2, 'imagecreatefromstring' => 2, 'imagecreatefromwbmp' => 2, 'imagecreatefromxbm' => 2, 'imagecreatetruecolor' => 2, 'imagedashedline' => 2, + 'imagedestroy' => 2, 'imageellipse' => 2, 'imagefill' => 2, 'imagefilledarc' => 2, 'imagefilledellipse' => 2, 'imagefilledpolygon' => 2, 'imagefilledrectangle' => 2, 'imagefilltoborder' => 2, 'imagefilter' => 2, 'imagefontheight' => 2, 'imagefontwidth' => 2, 'imageftbbox' => 2, 'imagefttext' => 2, 'imagegammacorrect' => 2, 'imagegd' => 2, 'imagegd2' => 2, 'imagegif' => 2, 'imagegrabscreen' => 2, 'imagegrabwindow' => 2, 'imageinterlace' => 2, 'imageistruecolor' => 2, 'imagejpeg' => 2, 'imagelayereffect' => 2, 'imageline' => 2, 'imageloadfont' => 2, 'imagepalettecopy' => 2, + 'imagepng' => 2, 'imagepolygon' => 2, 'imagepsbbox' => 2, 'imagepsencodefont' => 2, 'imagepsextendfont' => 2, 'imagepsfreefont' => 2, 'imagepsloadfont' => 2, 'imagepsslantfont' => 2, 'imagepstext' => 2, 'imagerectangle' => 2, 'imagerotate' => 2, 'imagesavealpha' => 2, 'imagesetbrush' => 2, 'imagesetpixel' => 2, 'imagesetstyle' => 2, 'imagesetthickness' => 2, 'imagesettile' => 2, 'imagestring' => 2, 'imagestringup' => 2, 'imagesx' => 2, 'imagesy' => 2, 'imagetruecolortopalette' => 2, 'imagettfbbox' => 2, 'imagettftext' => 2, 'imagetypes' => 2, 'imagewbmp' => 2, + 'imagexbm' => 2, 'imap_8bit' => 2, 'imap_alerts' => 2, 'imap_append' => 2, 'imap_base64' => 2, 'imap_binary' => 2, 'imap_body' => 2, 'imap_bodystruct' => 2, 'imap_check' => 2, 'imap_clearflag_full' => 2, 'imap_close' => 2, 'imap_create' => 2, 'imap_createmailbox' => 2, 'imap_delete' => 2, 'imap_deletemailbox' => 2, 'imap_errors' => 2, 'imap_expunge' => 2, 'imap_fetch_overview' => 2, 'imap_fetchbody' => 2, 'imap_fetchheader' => 2, 'imap_fetchmime' => 2, 'imap_fetchstructure' => 2, 'imap_fetchtext' => 2, 'imap_gc' => 2, 'imap_get_quota' => 2, 'imap_get_quotaroot' => 2, + 'imap_getacl' => 2, 'imap_getmailboxes' => 2, 'imap_getsubscribed' => 2, 'imap_header' => 2, 'imap_headerinfo' => 2, 'imap_headers' => 2, 'imap_last_error' => 2, 'imap_list' => 2, 'imap_listmailbox' => 2, 'imap_listscan' => 2, 'imap_listsubscribed' => 2, 'imap_lsub' => 2, 'imap_mail' => 2, 'imap_mail_compose' => 2, 'imap_mail_copy' => 2, 'imap_mail_move' => 2, 'imap_mailboxmsginfo' => 2, 'imap_mime_header_decode' => 2, 'imap_msgno' => 2, 'imap_mutf7_to_utf8' => 2, 'imap_num_msg' => 2, 'imap_num_recent' => 2, 'imap_open' => 2, 'imap_ping' => 2, 'imap_qprint' => 2, 'imap_rename' => 2, + 'imap_renamemailbox' => 2, 'imap_reopen' => 2, 'imap_rfc822_parse_adrlist' => 2, 'imap_rfc822_parse_headers' => 2, 'imap_rfc822_write_address' => 2, 'imap_savebody' => 2, 'imap_scan' => 2, 'imap_scanmailbox' => 2, 'imap_search' => 2, 'imap_set_quota' => 2, 'imap_setacl' => 2, 'imap_setflag_full' => 2, 'imap_sort' => 2, 'imap_status' => 2, 'imap_subscribe' => 2, 'imap_thread' => 2, 'imap_timeout' => 2, 'imap_uid' => 2, 'imap_undelete' => 2, 'imap_unsubscribe' => 2, 'imap_utf7_decode' => 2, 'imap_utf7_encode' => 2, 'imap_utf8' => 2, 'imap_utf8_to_mutf7' => 2, 'implode' => 2, 'import_request_variables' => 2, + 'in_array' => 2, 'inet_ntop' => 2, 'inet_pton' => 2, 'ini_alter' => 2, 'ini_get' => 2, 'ini_get_all' => 2, 'ini_restore' => 2, 'ini_set' => 2, 'interface_exists' => 2, 'intl_error_name' => 2, 'intl_get_error_code' => 2, 'intl_get_error_message' => 2, 'intl_is_failure' => 2, 'intval' => 2, 'ip2long' => 2, 'iptcembed' => 2, 'iptcparse' => 2, 'is_a' => 2, 'is_array' => 2, 'is_bool' => 2, 'is_callable' => 2, 'is_dir' => 2, 'is_double' => 2, 'is_executable' => 2, 'is_file' => 2, 'is_finite' => 2, + 'is_float' => 2, 'is_infinite' => 2, 'is_int' => 2, 'is_integer' => 2, 'is_link' => 2, 'is_long' => 2, 'is_nan' => 2, 'is_null' => 2, 'is_numeric' => 2, 'is_object' => 2, 'is_readable' => 2, 'is_real' => 2, 'is_resource' => 2, 'is_scalar' => 2, 'is_soap_fault' => 2, 'is_string' => 2, 'is_subclass_of' => 2, 'is_uploaded_file' => 2, 'is_writable' => 2, 'is_writeable' => 2, 'iterator_apply' => 2, 'iterator_count' => 2, 'iterator_to_array' => 2, 'java_last_exception_clear' => 2, 'java_last_exception_get' => 2, 'jddayofweek' => 2, + 'jdmonthname' => 2, 'jdtofrench' => 2, 'jdtogregorian' => 2, 'jdtojewish' => 2, 'jdtojulian' => 2, 'jdtounix' => 2, 'jewishtojd' => 2, 'join' => 2, 'jpeg2wbmp' => 2, 'json_decode' => 2, 'json_encode' => 2, 'json_last_error' => 2, 'juliantojd' => 2, 'key' => 2, 'key_exists' => 2, 'krsort' => 2, 'ksort' => 2, 'lcfirst' => 2, 'lcg_value' => 2, 'ldap_add' => 2, 'ldap_bind' => 2, 'ldap_close' => 2, 'ldap_compare' => 2, 'ldap_connect' => 2, 'ldap_count_entries' => 2, 'ldap_delete' => 2, + 'ldap_dn2ufn' => 2, 'ldap_err2str' => 2, 'ldap_errno' => 2, 'ldap_error' => 2, 'ldap_explode_dn' => 2, 'ldap_first_attribute' => 2, 'ldap_first_entry' => 2, 'ldap_first_reference' => 2, 'ldap_free_result' => 2, 'ldap_get_attributes' => 2, 'ldap_get_dn' => 2, 'ldap_get_entries' => 2, 'ldap_get_option' => 2, 'ldap_get_values' => 2, 'ldap_get_values_len' => 2, 'ldap_list' => 2, 'ldap_mod_add' => 2, 'ldap_mod_del' => 2, 'ldap_mod_replace' => 2, 'ldap_modify' => 2, 'ldap_next_attribute' => 2, 'ldap_next_entry' => 2, 'ldap_next_reference' => 2, 'ldap_parse_reference' => 2, 'ldap_parse_result' => 2, 'ldap_read' => 2, + 'ldap_rename' => 2, 'ldap_search' => 2, 'ldap_set_option' => 2, 'ldap_sort' => 2, 'ldap_start_tls' => 2, 'ldap_unbind' => 2, 'leak' => 2, 'levenshtein' => 2, 'libxml_clear_errors' => 2, 'libxml_disable_entity_loader' => 2, 'libxml_get_errors' => 2, 'libxml_get_last_error' => 2, 'libxml_set_streams_context' => 2, 'libxml_use_internal_errors' => 2, 'link' => 2, 'linkinfo' => 2, 'locale_accept_from_http' => 2, 'locale_canonicalize' => 2, 'locale_compose' => 2, 'locale_filter_matches' => 2, 'locale_get_all_variants' => 2, 'locale_get_default' => 2, 'locale_get_display_language' => 2, 'locale_get_display_name' => 2, 'locale_get_display_region' => 2, 'locale_get_display_script' => 2, + 'locale_get_display_variant' => 2, 'locale_get_keywords' => 2, 'locale_get_primary_language' => 2, 'locale_get_region' => 2, 'locale_get_script' => 2, 'locale_lookup' => 2, 'locale_parse' => 2, 'locale_set_default' => 2, 'localeconv' => 2, 'localtime' => 2, 'log' => 2, 'log10' => 2, 'log1p' => 2, 'long2ip' => 2, 'lstat' => 2, 'ltrim' => 2, 'lzf_compress' => 2, 'lzf_decompress' => 2, 'lzf_optimized_for' => 2, 'magic_quotes_runtime' => 2, 'mail' => 2, 'max' => 2, 'mb_check_encoding' => 2, 'mb_convert_case' => 2, 'mb_convert_encoding' => 2, 'mb_convert_kana' => 2, + 'mb_convert_variables' => 2, 'mb_decode_mimeheader' => 2, 'mb_decode_numericentity' => 2, 'mb_detect_encoding' => 2, 'mb_detect_order' => 2, 'mb_encode_mimeheader' => 2, 'mb_encode_numericentity' => 2, 'mb_encoding_aliases' => 2, 'mb_ereg' => 2, 'mb_ereg_match' => 2, 'mb_ereg_replace' => 2, 'mb_ereg_search' => 2, 'mb_ereg_search_getpos' => 2, 'mb_ereg_search_getregs' => 2, 'mb_ereg_search_init' => 2, 'mb_ereg_search_pos' => 2, 'mb_ereg_search_regs' => 2, 'mb_ereg_search_setpos' => 2, 'mb_eregi' => 2, 'mb_eregi_replace' => 2, 'mb_get_info' => 2, 'mb_http_input' => 2, 'mb_http_output' => 2, 'mb_internal_encoding' => 2, 'mb_language' => 2, 'mb_list_encodings' => 2, + 'mb_output_handler' => 2, 'mb_parse_str' => 2, 'mb_preferred_mime_name' => 2, 'mb_regex_encoding' => 2, 'mb_regex_set_options' => 2, 'mb_send_mail' => 2, 'mb_split' => 2, 'mb_strcut' => 2, 'mb_strimwidth' => 2, 'mb_stripos' => 2, 'mb_stristr' => 2, 'mb_strlen' => 2, 'mb_strpos' => 2, 'mb_strrchr' => 2, 'mb_strrichr' => 2, 'mb_strripos' => 2, 'mb_strrpos' => 2, 'mb_strstr' => 2, 'mb_strtolower' => 2, 'mb_strtoupper' => 2, 'mb_strwidth' => 2, 'mb_substitute_character' => 2, 'mb_substr' => 2, 'mb_substr_count' => 2, 'mbereg' => 2, 'mbereg_match' => 2, + 'mbereg_replace' => 2, 'mbereg_search' => 2, 'mbereg_search_getpos' => 2, 'mbereg_search_getregs' => 2, 'mbereg_search_init' => 2, 'mbereg_search_pos' => 2, 'mbereg_search_regs' => 2, 'mbereg_search_setpos' => 2, 'mberegi' => 2, 'mberegi_replace' => 2, 'mbregex_encoding' => 2, 'mbsplit' => 2, 'mbstrcut' => 2, 'mbstrlen' => 2, 'mbstrpos' => 2, 'mbstrrpos' => 2, 'mbsubstr' => 2, 'mcal_close' => 2, 'mcal_date_compare' => 2, 'mcal_date_valid' => 2, 'mcal_day_of_week' => 2, 'mcal_day_of_year' => 2, 'mcal_days_in_month' => 2, 'mcal_delete_event' => 2, 'mcal_event_init' => 2, 'mcal_event_set_alarm' => 2, + 'mcal_event_set_category' => 2, 'mcal_event_set_class' => 2, 'mcal_event_set_description' => 2, 'mcal_event_set_end' => 2, 'mcal_event_set_recur_daily' => 2, 'mcal_event_set_recur_monthly_mday' => 2, 'mcal_event_set_recur_monthly_wday' => 2, 'mcal_event_set_recur_weekly' => 2, 'mcal_event_set_recur_yearly' => 2, 'mcal_event_set_start' => 2, 'mcal_event_set_title' => 2, 'mcal_fetch_current_stream_event' => 2, 'mcal_fetch_event' => 2, 'mcal_is_leap_year' => 2, 'mcal_list_alarms' => 2, 'mcal_list_events' => 2, 'mcal_next_recurrence' => 2, 'mcal_open' => 2, 'mcal_snooze' => 2, 'mcal_store_event' => 2, 'mcal_time_valid' => 2, 'mcrypt_cbc' => 2, 'mcrypt_cfb' => 2, 'mcrypt_create_iv' => 2, 'mcrypt_decrypt' => 2, 'mcrypt_ecb' => 2, + 'mcrypt_enc_get_algorithms_name' => 2, 'mcrypt_enc_get_block_size' => 2, 'mcrypt_enc_get_iv_size' => 2, 'mcrypt_enc_get_key_size' => 2, 'mcrypt_enc_get_modes_name' => 2, 'mcrypt_enc_get_supported_key_sizes' => 2, 'mcrypt_enc_is_block_algorithm' => 2, 'mcrypt_enc_is_block_algorithm_mode' => 2, 'mcrypt_enc_is_block_mode' => 2, 'mcrypt_enc_self_test' => 2, 'mcrypt_encrypt' => 2, 'mcrypt_generic' => 2, 'mcrypt_generic_deinit' => 2, 'mcrypt_generic_end' => 2, 'mcrypt_generic_init' => 2, 'mcrypt_get_block_size' => 2, 'mcrypt_get_cipher_name' => 2, 'mcrypt_get_iv_size' => 2, 'mcrypt_get_key_size' => 2, 'mcrypt_list_algorithms' => 2, 'mcrypt_list_modes' => 2, 'mcrypt_module_close' => 2, 'mcrypt_module_get_algo_block_size' => 2, 'mcrypt_module_get_algo_key_size' => 2, 'mcrypt_module_get_supported_key_sizes' => 2, 'mcrypt_module_is_block_algorithm' => 2, + 'mcrypt_module_is_block_algorithm_mode' => 2, 'mcrypt_module_is_block_mode' => 2, 'mcrypt_module_open' => 2, 'mcrypt_module_self_test' => 2, 'mcrypt_ofb' => 2, 'md5' => 2, 'md5_file' => 2, 'mdecrypt_generic' => 2, 'memcache_add' => 2, 'memcache_add_server' => 2, 'memcache_close' => 2, 'memcache_connect' => 2, 'memcache_debug' => 2, 'memcache_decrement' => 2, 'memcache_delete' => 2, 'memcache_flush' => 2, 'memcache_get' => 2, 'memcache_get_extended_stats' => 2, 'memcache_get_server_status' => 2, 'memcache_get_stats' => 2, 'memcache_get_version' => 2, 'memcache_increment' => 2, 'memcache_pconnect' => 2, 'memcache_replace' => 2, 'memcache_set' => 2, 'memcache_set_compress_threshold' => 2, + 'memcache_set_server_params' => 2, 'memory_get_peak_usage' => 2, 'memory_get_usage' => 2, 'metaphone' => 2, 'method_exists' => 2, 'mhash' => 2, 'mhash_count' => 2, 'mhash_get_block_size' => 2, 'mhash_get_hash_name' => 2, 'mhash_keygen_s2k' => 2, 'microtime' => 2, 'mime_content_type' => 2, 'min' => 2, 'ming_keypress' => 2, 'ming_setcubicthreshold' => 2, 'ming_setscale' => 2, 'ming_useconstants' => 2, 'ming_useswfversion' => 2, 'mkdir' => 2, 'mktime' => 2, 'modifiers' => 2, 'money_format' => 2, 'move_uploaded_file' => 2, 'msgfmt_create' => 2, 'msgfmt_format' => 2, 'msgfmt_format_message' => 2, + 'msgfmt_get_error_code' => 2, 'msgfmt_get_error_message' => 2, 'msgfmt_get_locale' => 2, 'msgfmt_get_pattern' => 2, 'msgfmt_parse' => 2, 'msgfmt_parse_message' => 2, 'msgfmt_set_pattern' => 2, 'msql' => 2, 'msql_affected_rows' => 2, 'msql_close' => 2, 'msql_connect' => 2, 'msql_create_db' => 2, 'msql_createdb' => 2, 'msql_data_seek' => 2, 'msql_db_query' => 2, 'msql_dbname' => 2, 'msql_drop_db' => 2, 'msql_dropdb' => 2, 'msql_error' => 2, 'msql_fetch_array' => 2, 'msql_fetch_field' => 2, 'msql_fetch_object' => 2, 'msql_fetch_row' => 2, 'msql_field_flags' => 2, 'msql_field_len' => 2, 'msql_field_name' => 2, + 'msql_field_seek' => 2, 'msql_field_table' => 2, 'msql_field_type' => 2, 'msql_fieldflags' => 2, 'msql_fieldlen' => 2, 'msql_fieldname' => 2, 'msql_fieldtable' => 2, 'msql_fieldtype' => 2, 'msql_free_result' => 2, 'msql_freeresult' => 2, 'msql_list_dbs' => 2, 'msql_list_fields' => 2, 'msql_list_tables' => 2, 'msql_listdbs' => 2, 'msql_listfields' => 2, 'msql_listtables' => 2, 'msql_num_fields' => 2, 'msql_num_rows' => 2, 'msql_numfields' => 2, 'msql_numrows' => 2, 'msql_pconnect' => 2, 'msql_query' => 2, 'msql_regcase' => 2, 'msql_result' => 2, 'msql_select_db' => 2, 'msql_selectdb' => 2, + 'msql_tablename' => 2, 'mssql_bind' => 2, 'mssql_close' => 2, 'mssql_connect' => 2, 'mssql_data_seek' => 2, 'mssql_execute' => 2, 'mssql_fetch_array' => 2, 'mssql_fetch_assoc' => 2, 'mssql_fetch_batch' => 2, 'mssql_fetch_field' => 2, 'mssql_fetch_object' => 2, 'mssql_fetch_row' => 2, 'mssql_field_length' => 2, 'mssql_field_name' => 2, 'mssql_field_seek' => 2, 'mssql_field_type' => 2, 'mssql_free_result' => 2, 'mssql_free_statement' => 2, 'mssql_get_last_message' => 2, 'mssql_guid_string' => 2, 'mssql_init' => 2, 'mssql_min_error_severity' => 2, 'mssql_min_message_severity' => 2, 'mssql_next_result' => 2, 'mssql_num_fields' => 2, 'mssql_num_rows' => 2, + 'mssql_pconnect' => 2, 'mssql_query' => 2, 'mssql_result' => 2, 'mssql_rows_affected' => 2, 'mssql_select_db' => 2, 'mt_getrandmax' => 2, 'mt_rand' => 2, 'mt_srand' => 2, 'mysql' => 2, 'mysql_affected_rows' => 2, 'mysql_change_user' => 2, 'mysql_client_encoding' => 2, 'mysql_close' => 2, 'mysql_connect' => 2, 'mysql_create_db' => 2, 'mysql_createdb' => 2, 'mysql_data_seek' => 2, 'mysql_db_name' => 2, 'mysql_db_query' => 2, 'mysql_dbname' => 2, 'mysql_drop_db' => 2, 'mysql_dropdb' => 2, 'mysql_errno' => 2, 'mysql_error' => 2, 'mysql_escape_string' => 2, 'mysql_fetch_array' => 2, + 'mysql_fetch_assoc' => 2, 'mysql_fetch_field' => 2, 'mysql_fetch_lengths' => 2, 'mysql_fetch_object' => 2, 'mysql_fetch_row' => 2, 'mysql_field_flags' => 2, 'mysql_field_len' => 2, 'mysql_field_name' => 2, 'mysql_field_seek' => 2, 'mysql_field_table' => 2, 'mysql_field_type' => 2, 'mysql_fieldflags' => 2, 'mysql_fieldlen' => 2, 'mysql_fieldname' => 2, 'mysql_fieldtable' => 2, 'mysql_fieldtype' => 2, 'mysql_free_result' => 2, 'mysql_freeresult' => 2, 'mysql_get_client_info' => 2, 'mysql_get_host_info' => 2, 'mysql_get_proto_info' => 2, 'mysql_get_server_info' => 2, 'mysql_info' => 2, 'mysql_insert_id' => 2, 'mysql_list_dbs' => 2, 'mysql_list_fields' => 2, + 'mysql_list_processes' => 2, 'mysql_list_tables' => 2, 'mysql_listdbs' => 2, 'mysql_listfields' => 2, 'mysql_listtables' => 2, 'mysql_num_fields' => 2, 'mysql_num_rows' => 2, 'mysql_numfields' => 2, 'mysql_numrows' => 2, 'mysql_pconnect' => 2, 'mysql_ping' => 2, 'mysql_query' => 2, 'mysql_real_escape_string' => 2, 'mysql_result' => 2, 'mysql_select_db' => 2, 'mysql_selectdb' => 2, 'mysql_set_charset' => 2, 'mysql_stat' => 2, 'mysql_table_name' => 2, 'mysql_tablename' => 2, 'mysql_thread_id' => 2, 'mysql_unbuffered_query' => 2, 'mysqli_affected_rows' => 2, 'mysqli_autocommit' => 2, 'mysqli_bind_param' => 2, 'mysqli_bind_result' => 2, + 'mysqli_change_user' => 2, 'mysqli_character_set_name' => 2, 'mysqli_client_encoding' => 2, 'mysqli_close' => 2, 'mysqli_commit' => 2, 'mysqli_connect' => 2, 'mysqli_connect_errno' => 2, 'mysqli_connect_error' => 2, 'mysqli_data_seek' => 2, 'mysqli_debug' => 2, 'mysqli_disable_reads_from_master' => 2, 'mysqli_disable_rpl_parse' => 2, 'mysqli_dump_debug_info' => 2, 'mysqli_embedded_server_end' => 2, 'mysqli_embedded_server_start' => 2, 'mysqli_enable_reads_from_master' => 2, 'mysqli_enable_rpl_parse' => 2, 'mysqli_errno' => 2, 'mysqli_error' => 2, 'mysqli_escape_string' => 2, 'mysqli_execute' => 2, 'mysqli_fetch' => 2, 'mysqli_fetch_all' => 2, 'mysqli_fetch_array' => 2, 'mysqli_fetch_assoc' => 2, 'mysqli_fetch_field' => 2, + 'mysqli_fetch_field_direct' => 2, 'mysqli_fetch_fields' => 2, 'mysqli_fetch_lengths' => 2, 'mysqli_fetch_object' => 2, 'mysqli_fetch_row' => 2, 'mysqli_field_count' => 2, 'mysqli_field_seek' => 2, 'mysqli_field_tell' => 2, 'mysqli_free_result' => 2, 'mysqli_get_cache_stats' => 2, 'mysqli_get_charset' => 2, 'mysqli_get_client_info' => 2, 'mysqli_get_client_stats' => 2, 'mysqli_get_client_version' => 2, 'mysqli_get_connection_stats' => 2, 'mysqli_get_host_info' => 2, 'mysqli_get_metadata' => 2, 'mysqli_get_proto_info' => 2, 'mysqli_get_server_info' => 2, 'mysqli_get_server_version' => 2, 'mysqli_get_warnings' => 2, 'mysqli_info' => 2, 'mysqli_init' => 2, 'mysqli_insert_id' => 2, 'mysqli_kill' => 2, 'mysqli_master_query' => 2, + 'mysqli_more_results' => 2, 'mysqli_multi_query' => 2, 'mysqli_next_result' => 2, 'mysqli_num_fields' => 2, 'mysqli_num_rows' => 2, 'mysqli_options' => 2, 'mysqli_param_count' => 2, 'mysqli_ping' => 2, 'mysqli_poll' => 2, 'mysqli_prepare' => 2, 'mysqli_query' => 2, 'mysqli_real_connect' => 2, 'mysqli_real_escape_string' => 2, 'mysqli_real_query' => 2, 'mysqli_reap_async_query' => 2, 'mysqli_refresh' => 2, 'mysqli_report' => 2, 'mysqli_rollback' => 2, 'mysqli_rpl_parse_enabled' => 2, 'mysqli_rpl_probe' => 2, 'mysqli_rpl_query_type' => 2, 'mysqli_select_db' => 2, 'mysqli_send_long_data' => 2, 'mysqli_send_query' => 2, 'mysqli_set_charset' => 2, 'mysqli_set_local_infile_default' => 2, + 'mysqli_set_local_infile_handler' => 2, 'mysqli_set_opt' => 2, 'mysqli_slave_query' => 2, 'mysqli_sqlstate' => 2, 'mysqli_ssl_set' => 2, 'mysqli_stat' => 2, 'mysqli_stmt_affected_rows' => 2, 'mysqli_stmt_attr_get' => 2, 'mysqli_stmt_attr_set' => 2, 'mysqli_stmt_bind_param' => 2, 'mysqli_stmt_bind_result' => 2, 'mysqli_stmt_close' => 2, 'mysqli_stmt_data_seek' => 2, 'mysqli_stmt_errno' => 2, 'mysqli_stmt_error' => 2, 'mysqli_stmt_execute' => 2, 'mysqli_stmt_fetch' => 2, 'mysqli_stmt_field_count' => 2, 'mysqli_stmt_free_result' => 2, 'mysqli_stmt_get_result' => 2, 'mysqli_stmt_get_warnings' => 2, 'mysqli_stmt_init' => 2, 'mysqli_stmt_insert_id' => 2, 'mysqli_stmt_more_results' => 2, 'mysqli_stmt_next_result' => 2, 'mysqli_stmt_num_rows' => 2, + 'mysqli_stmt_param_count' => 2, 'mysqli_stmt_prepare' => 2, 'mysqli_stmt_reset' => 2, 'mysqli_stmt_result_metadata' => 2, 'mysqli_stmt_send_long_data' => 2, 'mysqli_stmt_sqlstate' => 2, 'mysqli_stmt_store_result' => 2, 'mysqli_store_result' => 2, 'mysqli_thread_id' => 2, 'mysqli_thread_safe' => 2, 'mysqli_use_result' => 2, 'mysqli_warning_count' => 2, 'natcasesort' => 2, 'natsort' => 2, 'new_xmldoc' => 2, 'next' => 2, 'ngettext' => 2, 'nl2br' => 2, 'nl_langinfo' => 2, 'normalizer_is_normalized' => 2, 'normalizer_normalize' => 2, 'ntuser_getdomaincontroller' => 2, 'ntuser_getusergroups' => 2, 'ntuser_getuserinfo' => 2, 'ntuser_getuserlist' => 2, 'number_format' => 2, + 'numfmt_create' => 2, 'numfmt_format' => 2, 'numfmt_format_currency' => 2, 'numfmt_get_attribute' => 2, 'numfmt_get_error_code' => 2, 'numfmt_get_error_message' => 2, 'numfmt_get_locale' => 2, 'numfmt_get_pattern' => 2, 'numfmt_get_symbol' => 2, 'numfmt_get_text_attribute' => 2, 'numfmt_parse' => 2, 'numfmt_parse_currency' => 2, 'numfmt_set_attribute' => 2, 'numfmt_set_pattern' => 2, 'numfmt_set_symbol' => 2, 'numfmt_set_text_attribute' => 2, 'ob_clean' => 2, 'ob_deflatehandler' => 2, 'ob_end_clean' => 2, 'ob_end_flush' => 2, 'ob_etaghandler' => 2, 'ob_flush' => 2, 'ob_get_clean' => 2, 'ob_get_contents' => 2, 'ob_get_flush' => 2, 'ob_get_length' => 2, + 'ob_get_level' => 2, 'ob_get_status' => 2, 'ob_gzhandler' => 2, 'ob_iconv_handler' => 2, 'ob_implicit_flush' => 2, 'ob_inflatehandler' => 2, 'ob_list_handlers' => 2, 'ob_start' => 2, 'ob_tidyhandler' => 2, 'ocibindbyname' => 2, 'ocicolumnisnull' => 2, 'ocicolumnname' => 2, 'ocicolumnsize' => 2, 'ocicolumntype' => 2, 'ocicommit' => 2, 'ocidefinebyname' => 2, 'ocierror' => 2, 'ociexecute' => 2, 'ocifetch' => 2, 'ocifetchinto' => 2, 'ocifetchstatement' => 2, 'ocifreecursor' => 2, 'ocifreestatement' => 2, 'ociinternaldebug' => 2, 'ocilogoff' => 2, 'ocilogon' => 2, + 'ocinewcursor' => 2, 'ocinewdescriptor' => 2, 'ocinlogon' => 2, 'ocinumcols' => 2, 'ociparse' => 2, 'ociplogon' => 2, 'ociresult' => 2, 'ocirollback' => 2, 'ocirowcount' => 2, 'ociserverversion' => 2, 'ocistatementtype' => 2, 'octdec' => 2, 'odbc_autocommit' => 2, 'odbc_binmode' => 2, 'odbc_close' => 2, 'odbc_close_all' => 2, 'odbc_columnprivileges' => 2, 'odbc_columns' => 2, 'odbc_commit' => 2, 'odbc_connect' => 2, 'odbc_cursor' => 2, 'odbc_data_source' => 2, 'odbc_do' => 2, 'odbc_error' => 2, 'odbc_errormsg' => 2, 'odbc_exec' => 2, + 'odbc_execute' => 2, 'odbc_fetch_array' => 2, 'odbc_fetch_into' => 2, 'odbc_fetch_object' => 2, 'odbc_fetch_row' => 2, 'odbc_field_len' => 2, 'odbc_field_name' => 2, 'odbc_field_num' => 2, 'odbc_field_precision' => 2, 'odbc_field_scale' => 2, 'odbc_field_type' => 2, 'odbc_foreignkeys' => 2, 'odbc_free_result' => 2, 'odbc_gettypeinfo' => 2, 'odbc_longreadlen' => 2, 'odbc_next_result' => 2, 'odbc_num_fields' => 2, 'odbc_num_rows' => 2, 'odbc_pconnect' => 2, 'odbc_prepare' => 2, 'odbc_primarykeys' => 2, 'odbc_procedurecolumns' => 2, 'odbc_procedures' => 2, 'odbc_result' => 2, 'odbc_result_all' => 2, 'odbc_rollback' => 2, + 'odbc_setoption' => 2, 'odbc_specialcolumns' => 2, 'odbc_statistics' => 2, 'odbc_tableprivileges' => 2, 'odbc_tables' => 2, 'opendir' => 2, 'openlog' => 2, 'openssl_cipher_iv_length' => 2, 'openssl_csr_export' => 2, 'openssl_csr_export_to_file' => 2, 'openssl_csr_get_public_key' => 2, 'openssl_csr_get_subject' => 2, 'openssl_csr_new' => 2, 'openssl_csr_sign' => 2, 'openssl_decrypt' => 2, 'openssl_dh_compute_key' => 2, 'openssl_digest' => 2, 'openssl_encrypt' => 2, 'openssl_error_string' => 2, 'openssl_free_key' => 2, 'openssl_get_cipher_methods' => 2, 'openssl_get_md_methods' => 2, 'openssl_get_privatekey' => 2, 'openssl_get_publickey' => 2, 'openssl_open' => 2, 'openssl_pkcs12_export' => 2, + 'openssl_pkcs12_export_to_file' => 2, 'openssl_pkcs12_read' => 2, 'openssl_pkcs7_decrypt' => 2, 'openssl_pkcs7_encrypt' => 2, 'openssl_pkcs7_sign' => 2, 'openssl_pkcs7_verify' => 2, 'openssl_pkey_export' => 2, 'openssl_pkey_export_to_file' => 2, 'openssl_pkey_free' => 2, 'openssl_pkey_get_details' => 2, 'openssl_pkey_get_private' => 2, 'openssl_pkey_get_public' => 2, 'openssl_pkey_new' => 2, 'openssl_private_decrypt' => 2, 'openssl_private_encrypt' => 2, 'openssl_public_decrypt' => 2, 'openssl_public_encrypt' => 2, 'openssl_random_pseudo_bytes' => 2, 'openssl_seal' => 2, 'openssl_sign' => 2, 'openssl_verify' => 2, 'openssl_x509_check_private_key' => 2, 'openssl_x509_checkpurpose' => 2, 'openssl_x509_export' => 2, 'openssl_x509_export_to_file' => 2, 'openssl_x509_free' => 2, + 'openssl_x509_parse' => 2, 'openssl_x509_read' => 2, 'ora_bind' => 2, 'ora_close' => 2, 'ora_columnname' => 2, 'ora_columntype' => 2, 'ora_commit' => 2, 'ora_commitoff' => 2, 'ora_commiton' => 2, 'ora_error' => 2, 'ora_errorcode' => 2, 'ora_exec' => 2, 'ora_fetch' => 2, 'ora_getcolumn' => 2, 'ora_logoff' => 2, 'ora_logon' => 2, 'ora_open' => 2, 'ora_parse' => 2, 'ora_rollback' => 2, 'ord' => 2, 'output_add_rewrite_var' => 2, 'output_reset_rewrite_vars' => 2, 'outputdebugstring' => 2, 'overload' => 2, 'pack' => 2, 'parse_ini_file' => 2, + 'parse_ini_string' => 2, 'parse_str' => 2, 'parse_url' => 2, 'parsekit_compile_file' => 2, 'parsekit_compile_string' => 2, 'parsekit_func_arginfo' => 2, 'parsekit_opcode_flags' => 2, 'parsekit_opcode_name' => 2, 'passthru' => 2, 'pathinfo' => 2, 'pattern' => 2, 'pclose' => 2, 'pdf_add_annotation' => 2, 'pdf_add_bookmark' => 2, 'pdf_add_launchlink' => 2, 'pdf_add_locallink' => 2, 'pdf_add_nameddest' => 2, 'pdf_add_note' => 2, 'pdf_add_outline' => 2, 'pdf_add_pdflink' => 2, 'pdf_add_thumbnail' => 2, 'pdf_add_weblink' => 2, 'pdf_arc' => 2, 'pdf_arcn' => 2, 'pdf_attach_file' => 2, 'pdf_begin_font' => 2, + 'pdf_begin_glyph' => 2, 'pdf_begin_page' => 2, 'pdf_begin_pattern' => 2, 'pdf_begin_template' => 2, 'pdf_circle' => 2, 'pdf_clip' => 2, 'pdf_close' => 2, 'pdf_close_image' => 2, 'pdf_close_pdi' => 2, 'pdf_close_pdi_page' => 2, 'pdf_closepath' => 2, 'pdf_closepath_fill_stroke' => 2, 'pdf_closepath_stroke' => 2, 'pdf_concat' => 2, 'pdf_continue_text' => 2, 'pdf_create_gstate' => 2, 'pdf_create_pvf' => 2, 'pdf_curveto' => 2, 'pdf_delete' => 2, 'pdf_delete_pvf' => 2, 'pdf_encoding_set_char' => 2, 'pdf_end_font' => 2, 'pdf_end_glyph' => 2, 'pdf_end_page' => 2, 'pdf_end_pattern' => 2, 'pdf_end_template' => 2, + 'pdf_endpath' => 2, 'pdf_execute_image' => 2, 'pdf_fill' => 2, 'pdf_fill_imageblock' => 2, 'pdf_fill_pdfblock' => 2, 'pdf_fill_stroke' => 2, 'pdf_fill_textblock' => 2, 'pdf_findfont' => 2, 'pdf_fit_image' => 2, 'pdf_fit_pdi_page' => 2, 'pdf_fit_textline' => 2, 'pdf_get_apiname' => 2, 'pdf_get_buffer' => 2, 'pdf_get_errmsg' => 2, 'pdf_get_errnum' => 2, 'pdf_get_info' => 2, 'pdf_get_parameter' => 2, 'pdf_get_pdi_parameter' => 2, 'pdf_get_pdi_value' => 2, 'pdf_get_value' => 2, 'pdf_initgraphics' => 2, 'pdf_lineto' => 2, 'pdf_load_font' => 2, 'pdf_load_iccprofile' => 2, 'pdf_load_image' => 2, 'pdf_makespotcolor' => 2, + 'pdf_moveto' => 2, 'pdf_new' => 2, 'pdf_open' => 2, 'pdf_open_ccitt' => 2, 'pdf_open_file' => 2, 'pdf_open_gif' => 2, 'pdf_open_image' => 2, 'pdf_open_image_file' => 2, 'pdf_open_jpeg' => 2, 'pdf_open_memory_image' => 2, 'pdf_open_pdi' => 2, 'pdf_open_pdi_page' => 2, 'pdf_place_image' => 2, 'pdf_place_pdi_page' => 2, 'pdf_process_pdi' => 2, 'pdf_put_image' => 2, 'pdf_rect' => 2, 'pdf_restore' => 2, 'pdf_rotate' => 2, 'pdf_save' => 2, 'pdf_scale' => 2, 'pdf_set_border_color' => 2, 'pdf_set_border_dash' => 2, 'pdf_set_border_style' => 2, 'pdf_set_char_spacing' => 2, 'pdf_set_duration' => 2, + 'pdf_set_font' => 2, 'pdf_set_gstate' => 2, 'pdf_set_horiz_scaling' => 2, 'pdf_set_info' => 2, 'pdf_set_info_author' => 2, 'pdf_set_info_creator' => 2, 'pdf_set_info_keywords' => 2, 'pdf_set_info_subject' => 2, 'pdf_set_info_title' => 2, 'pdf_set_leading' => 2, 'pdf_set_parameter' => 2, 'pdf_set_text_matrix' => 2, 'pdf_set_text_pos' => 2, 'pdf_set_text_rendering' => 2, 'pdf_set_text_rise' => 2, 'pdf_set_transition' => 2, 'pdf_set_value' => 2, 'pdf_set_word_spacing' => 2, 'pdf_setcolor' => 2, 'pdf_setdash' => 2, 'pdf_setdashpattern' => 2, 'pdf_setflat' => 2, 'pdf_setfont' => 2, 'pdf_setgray' => 2, 'pdf_setgray_fill' => 2, 'pdf_setgray_stroke' => 2, + 'pdf_setlinecap' => 2, 'pdf_setlinejoin' => 2, 'pdf_setlinewidth' => 2, 'pdf_setmatrix' => 2, 'pdf_setmiterlimit' => 2, 'pdf_setpolydash' => 2, 'pdf_setrgbcolor' => 2, 'pdf_setrgbcolor_fill' => 2, 'pdf_setrgbcolor_stroke' => 2, 'pdf_shading' => 2, 'pdf_shading_pattern' => 2, 'pdf_shfill' => 2, 'pdf_show' => 2, 'pdf_show_boxed' => 2, 'pdf_show_xy' => 2, 'pdf_skew' => 2, 'pdf_stringwidth' => 2, 'pdf_stroke' => 2, 'pdf_translate' => 2, 'pdo_drivers' => 2, 'pfsockopen' => 2, 'pg_affected_rows' => 2, 'pg_cancel_query' => 2, 'pg_client_encoding' => 2, 'pg_clientencoding' => 2, 'pg_close' => 2, + 'pg_cmdtuples' => 2, 'pg_connect' => 2, 'pg_connection_busy' => 2, 'pg_connection_reset' => 2, 'pg_connection_status' => 2, 'pg_convert' => 2, 'pg_copy_from' => 2, 'pg_copy_to' => 2, 'pg_dbname' => 2, 'pg_delete' => 2, 'pg_end_copy' => 2, 'pg_errormessage' => 2, 'pg_escape_bytea' => 2, 'pg_escape_string' => 2, 'pg_exec' => 2, 'pg_execute' => 2, 'pg_fetch_all' => 2, 'pg_fetch_all_columns' => 2, 'pg_fetch_array' => 2, 'pg_fetch_assoc' => 2, 'pg_fetch_object' => 2, 'pg_fetch_result' => 2, 'pg_fetch_row' => 2, 'pg_field_is_null' => 2, 'pg_field_name' => 2, 'pg_field_num' => 2, + 'pg_field_prtlen' => 2, 'pg_field_size' => 2, 'pg_field_table' => 2, 'pg_field_type' => 2, 'pg_field_type_oid' => 2, 'pg_fieldisnull' => 2, 'pg_fieldname' => 2, 'pg_fieldnum' => 2, 'pg_fieldprtlen' => 2, 'pg_fieldsize' => 2, 'pg_fieldtype' => 2, 'pg_free_result' => 2, 'pg_freeresult' => 2, 'pg_get_notify' => 2, 'pg_get_pid' => 2, 'pg_get_result' => 2, 'pg_getlastoid' => 2, 'pg_host' => 2, 'pg_insert' => 2, 'pg_last_error' => 2, 'pg_last_notice' => 2, 'pg_last_oid' => 2, 'pg_lo_close' => 2, 'pg_lo_create' => 2, 'pg_lo_export' => 2, 'pg_lo_import' => 2, + 'pg_lo_open' => 2, 'pg_lo_read' => 2, 'pg_lo_read_all' => 2, 'pg_lo_seek' => 2, 'pg_lo_tell' => 2, 'pg_lo_unlink' => 2, 'pg_lo_write' => 2, 'pg_loclose' => 2, 'pg_locreate' => 2, 'pg_loexport' => 2, 'pg_loimport' => 2, 'pg_loopen' => 2, 'pg_loread' => 2, 'pg_loreadall' => 2, 'pg_lounlink' => 2, 'pg_lowrite' => 2, 'pg_meta_data' => 2, 'pg_num_fields' => 2, 'pg_num_rows' => 2, 'pg_numfields' => 2, 'pg_numrows' => 2, 'pg_options' => 2, 'pg_parameter_status' => 2, 'pg_pconnect' => 2, 'pg_ping' => 2, 'pg_port' => 2, + 'pg_prepare' => 2, 'pg_put_line' => 2, 'pg_query' => 2, 'pg_query_params' => 2, 'pg_result' => 2, 'pg_result_error' => 2, 'pg_result_error_field' => 2, 'pg_result_seek' => 2, 'pg_result_status' => 2, 'pg_select' => 2, 'pg_send_execute' => 2, 'pg_send_prepare' => 2, 'pg_send_query' => 2, 'pg_send_query_params' => 2, 'pg_set_client_encoding' => 2, 'pg_set_error_verbosity' => 2, 'pg_setclientencoding' => 2, 'pg_trace' => 2, 'pg_transaction_status' => 2, 'pg_tty' => 2, 'pg_unescape_bytea' => 2, 'pg_untrace' => 2, 'pg_update' => 2, 'pg_version' => 2, 'php_egg_logo_guid' => 2, 'php_ini_loaded_file' => 2, + 'php_ini_scanned_files' => 2, 'php_logo_guid' => 2, 'php_real_logo_guid' => 2, 'php_sapi_name' => 2, 'php_strip_whitespace' => 2, 'php_uname' => 2, 'phpcredits' => 2, 'phpdoc_xml_from_string' => 2, 'phpinfo' => 2, 'phpversion' => 2, 'pi' => 2, 'png2wbmp' => 2, 'pop3_close' => 2, 'pop3_delete_message' => 2, 'pop3_get_account_size' => 2, 'pop3_get_message' => 2, 'pop3_get_message_count' => 2, 'pop3_get_message_header' => 2, 'pop3_get_message_ids' => 2, 'pop3_get_message_size' => 2, 'pop3_get_message_sizes' => 2, 'pop3_open' => 2, 'pop3_undelete' => 2, 'popen' => 2, 'pos' => 2, 'posix_ctermid' => 2, + 'posix_errno' => 2, 'posix_get_last_error' => 2, 'posix_getcwd' => 2, 'posix_getegid' => 2, 'posix_geteuid' => 2, 'posix_getgid' => 2, 'posix_getgrgid' => 2, 'posix_getgrnam' => 2, 'posix_getgroups' => 2, 'posix_getlogin' => 2, 'posix_getpgid' => 2, 'posix_getpgrp' => 2, 'posix_getpid' => 2, 'posix_getppid' => 2, 'posix_getpwnam' => 2, 'posix_getpwuid' => 2, 'posix_getrlimit' => 2, 'posix_getsid' => 2, 'posix_getuid' => 2, 'posix_isatty' => 2, 'posix_kill' => 2, 'posix_mkfifo' => 2, 'posix_setegid' => 2, 'posix_seteuid' => 2, 'posix_setgid' => 2, 'posix_setpgid' => 2, + 'posix_setsid' => 2, 'posix_setuid' => 2, 'posix_strerror' => 2, 'posix_times' => 2, 'posix_ttyname' => 2, 'posix_uname' => 2, 'pow' => 2, 'preg_filter' => 2, 'preg_grep' => 2, 'preg_last_error' => 2, 'preg_match' => 2, 'preg_match_all' => 2, 'preg_quote' => 2, 'preg_replace' => 2, 'preg_replace_callback' => 2, 'preg_split' => 2, 'prev' => 2, 'print_r' => 2, 'printf' => 2, 'proc_close' => 2, 'proc_get_status' => 2, 'proc_open' => 2, 'proc_terminate' => 2, 'property_exists' => 2, 'putenv' => 2, 'quoted_printable_decode' => 2, + 'quoted_printable_encode' => 2, 'quotemeta' => 2, 'rad2deg' => 2, 'radius_acct_open' => 2, 'radius_add_server' => 2, 'radius_auth_open' => 2, 'radius_close' => 2, 'radius_config' => 2, 'radius_create_request' => 2, 'radius_cvt_addr' => 2, 'radius_cvt_int' => 2, 'radius_cvt_string' => 2, 'radius_demangle' => 2, 'radius_demangle_mppe_key' => 2, 'radius_get_attr' => 2, 'radius_get_vendor_attr' => 2, 'radius_put_addr' => 2, 'radius_put_attr' => 2, 'radius_put_int' => 2, 'radius_put_string' => 2, 'radius_put_vendor_addr' => 2, 'radius_put_vendor_attr' => 2, 'radius_put_vendor_int' => 2, 'radius_put_vendor_string' => 2, 'radius_request_authenticator' => 2, 'radius_send_request' => 2, + 'radius_server_secret' => 2, 'radius_strerror' => 2, 'rand' => 2, 'range' => 2, 'rawurldecode' => 2, 'rawurlencode' => 2, 'read_exif_data' => 2, 'readdir' => 2, 'readfile' => 2, 'readgzfile' => 2, 'readlink' => 2, 'realpath' => 2, 'realpath_cache_get' => 2, 'realpath_cache_size' => 2, 'recode_file' => 2, 'recode_string' => 2, 'reg_close_key' => 2, 'reg_create_key' => 2, 'reg_enum_key' => 2, 'reg_enum_value' => 2, 'reg_get_value' => 2, 'reg_open_key' => 2, 'reg_set_value' => 2, 'register_shutdown_function' => 2, 'register_tick_function' => 2, 'rename' => 2, + 'res_close' => 2, 'res_get' => 2, 'res_list' => 2, 'res_list_type' => 2, 'res_open' => 2, 'res_set' => 2, 'reset' => 2, 'resourcebundle_count' => 2, 'resourcebundle_create' => 2, 'resourcebundle_get' => 2, 'resourcebundle_get_error_code' => 2, 'resourcebundle_get_error_message' => 2, 'resourcebundle_locales' => 2, 'restore_error_handler' => 2, 'restore_exception_handler' => 2, 'restore_include_path' => 2, 'rewind' => 2, 'rewinddir' => 2, 'rmdir' => 2, 'round' => 2, 'rsort' => 2, 'rtrim' => 2, 'runkit_class_adopt' => 2, 'runkit_class_emancipate' => 2, 'runkit_constant_add' => 2, 'runkit_constant_redefine' => 2, + 'runkit_constant_remove' => 2, 'runkit_default_property_add' => 2, 'runkit_function_add' => 2, 'runkit_function_copy' => 2, 'runkit_function_redefine' => 2, 'runkit_function_remove' => 2, 'runkit_function_rename' => 2, 'runkit_import' => 2, 'runkit_lint' => 2, 'runkit_lint_file' => 2, 'runkit_method_add' => 2, 'runkit_method_copy' => 2, 'runkit_method_redefine' => 2, 'runkit_method_remove' => 2, 'runkit_method_rename' => 2, 'runkit_object_id' => 2, 'runkit_return_value_used' => 2, 'runkit_sandbox_output_handler' => 2, 'runkit_superglobals' => 2, 'runkit_zval_inspect' => 2, 'scandir' => 2, 'sem_acquire' => 2, 'sem_get' => 2, 'sem_release' => 2, 'sem_remove' => 2, 'serialize' => 2, + 'session_cache_expire' => 2, 'session_cache_limiter' => 2, 'session_commit' => 2, 'session_decode' => 2, 'session_destroy' => 2, 'session_encode' => 2, 'session_get_cookie_params' => 2, 'session_id' => 2, 'session_is_registered' => 2, 'session_module_name' => 2, 'session_name' => 2, 'session_regenerate_id' => 2, 'session_register' => 2, 'session_save_path' => 2, 'session_set_cookie_params' => 2, 'session_set_save_handler' => 2, 'session_start' => 2, 'session_unregister' => 2, 'session_unset' => 2, 'session_write_close' => 2, 'set_content' => 2, 'set_error_handler' => 2, 'set_exception_handler' => 2, 'set_file_buffer' => 2, 'set_include_path' => 2, 'set_magic_quotes_runtime' => 2, + 'set_socket_blocking' => 2, 'set_time_limit' => 2, 'setcookie' => 2, 'setlocale' => 2, 'setrawcookie' => 2, 'settype' => 2, 'sha1' => 2, 'sha1_file' => 2, 'shell_exec' => 2, 'shm_attach' => 2, 'shm_detach' => 2, 'shm_get_var' => 2, 'shm_put_var' => 2, 'shm_remove' => 2, 'shm_remove_var' => 2, 'shmop_close' => 2, 'shmop_delete' => 2, 'shmop_open' => 2, 'shmop_read' => 2, 'shmop_size' => 2, 'shmop_write' => 2, 'show_source' => 2, 'shuffle' => 2, 'similar_text' => 2, 'simplexml_import_dom' => 2, 'simplexml_load_file' => 2, + 'simplexml_load_string' => 2, 'sin' => 2, 'sinh' => 2, 'sizeof' => 2, 'sleep' => 2, 'smtp_close' => 2, 'smtp_cmd_data' => 2, 'smtp_cmd_mail' => 2, 'smtp_cmd_rcpt' => 2, 'smtp_connect' => 2, 'snmp2_get' => 2, 'snmp2_getnext' => 2, 'snmp2_real_walk' => 2, 'snmp2_set' => 2, 'snmp2_walk' => 2, 'snmp3_get' => 2, 'snmp3_getnext' => 2, 'snmp3_real_walk' => 2, 'snmp3_set' => 2, 'snmp3_walk' => 2, 'snmp_get_quick_print' => 2, 'snmp_get_valueretrieval' => 2, 'snmp_read_mib' => 2, 'snmp_set_quick_print' => 2, 'snmp_set_valueretrieval' => 2, 'snmpget' => 2, + 'snmpgetnext' => 2, 'snmprealwalk' => 2, 'snmpset' => 2, 'snmpwalk' => 2, 'snmpwalkoid' => 2, 'socket_accept' => 2, 'socket_bind' => 2, 'socket_clear_error' => 2, 'socket_close' => 2, 'socket_connect' => 2, 'socket_create' => 2, 'socket_create_listen' => 2, 'socket_create_pair' => 2, 'socket_get_option' => 2, 'socket_get_status' => 2, 'socket_getopt' => 2, 'socket_getpeername' => 2, 'socket_getsockname' => 2, 'socket_iovec_add' => 2, 'socket_iovec_alloc' => 2, 'socket_iovec_delete' => 2, 'socket_iovec_fetch' => 2, 'socket_iovec_free' => 2, 'socket_iovec_set' => 2, 'socket_last_error' => 2, 'socket_listen' => 2, + 'socket_read' => 2, 'socket_readv' => 2, 'socket_recv' => 2, 'socket_recvfrom' => 2, 'socket_recvmsg' => 2, 'socket_select' => 2, 'socket_send' => 2, 'socket_sendmsg' => 2, 'socket_sendto' => 2, 'socket_set_block' => 2, 'socket_set_blocking' => 2, 'socket_set_nonblock' => 2, 'socket_set_option' => 2, 'socket_set_timeout' => 2, 'socket_setopt' => 2, 'socket_shutdown' => 2, 'socket_strerror' => 2, 'socket_write' => 2, 'socket_writev' => 2, 'solid_close' => 2, 'solid_connect' => 2, 'solid_exec' => 2, 'solid_fetchrow' => 2, 'solid_fieldname' => 2, 'solid_fieldnum' => 2, 'solid_freeresult' => 2, + 'solid_numfields' => 2, 'solid_numrows' => 2, 'solid_result' => 2, 'sort' => 2, 'soundex' => 2, 'spl_autoload' => 2, 'spl_autoload_call' => 2, 'spl_autoload_extensions' => 2, 'spl_autoload_functions' => 2, 'spl_autoload_register' => 2, 'spl_autoload_unregister' => 2, 'spl_classes' => 2, 'spl_object_hash' => 2, 'split' => 2, 'spliti' => 2, 'sprintf' => 2, 'sql_regcase' => 2, 'sqlite_array_query' => 2, 'sqlite_busy_timeout' => 2, 'sqlite_changes' => 2, 'sqlite_close' => 2, 'sqlite_column' => 2, 'sqlite_create_aggregate' => 2, 'sqlite_create_function' => 2, 'sqlite_current' => 2, 'sqlite_error_string' => 2, + 'sqlite_escape_string' => 2, 'sqlite_exec' => 2, 'sqlite_factory' => 2, 'sqlite_fetch_all' => 2, 'sqlite_fetch_array' => 2, 'sqlite_fetch_column_types' => 2, 'sqlite_fetch_object' => 2, 'sqlite_fetch_single' => 2, 'sqlite_fetch_string' => 2, 'sqlite_field_name' => 2, 'sqlite_has_more' => 2, 'sqlite_has_prev' => 2, 'sqlite_last_error' => 2, 'sqlite_last_insert_rowid' => 2, 'sqlite_libencoding' => 2, 'sqlite_libversion' => 2, 'sqlite_next' => 2, 'sqlite_num_fields' => 2, 'sqlite_num_rows' => 2, 'sqlite_open' => 2, 'sqlite_popen' => 2, 'sqlite_prev' => 2, 'sqlite_query' => 2, 'sqlite_rewind' => 2, 'sqlite_seek' => 2, 'sqlite_single_query' => 2, + 'sqlite_udf_decode_binary' => 2, 'sqlite_udf_encode_binary' => 2, 'sqlite_unbuffered_query' => 2, 'sqlite_valid' => 2, 'sqrt' => 2, 'srand' => 2, 'sscanf' => 2, 'ssh2_auth_hostbased_file' => 2, 'ssh2_auth_none' => 2, 'ssh2_auth_password' => 2, 'ssh2_auth_pubkey_file' => 2, 'ssh2_connect' => 2, 'ssh2_exec' => 2, 'ssh2_fetch_stream' => 2, 'ssh2_fingerprint' => 2, 'ssh2_forward_accept' => 2, 'ssh2_forward_listen' => 2, 'ssh2_methods_negotiated' => 2, 'ssh2_poll' => 2, 'ssh2_publickey_add' => 2, 'ssh2_publickey_init' => 2, 'ssh2_publickey_list' => 2, 'ssh2_publickey_remove' => 2, 'ssh2_scp_recv' => 2, 'ssh2_scp_send' => 2, 'ssh2_sftp' => 2, + 'ssh2_sftp_lstat' => 2, 'ssh2_sftp_mkdir' => 2, 'ssh2_sftp_readlink' => 2, 'ssh2_sftp_realpath' => 2, 'ssh2_sftp_rename' => 2, 'ssh2_sftp_rmdir' => 2, 'ssh2_sftp_stat' => 2, 'ssh2_sftp_symlink' => 2, 'ssh2_sftp_unlink' => 2, 'ssh2_shell' => 2, 'ssh2_tunnel' => 2, 'stat' => 2, 'stats_absolute_deviation' => 2, 'stats_cdf_beta' => 2, 'stats_cdf_binomial' => 2, 'stats_cdf_cauchy' => 2, 'stats_cdf_chisquare' => 2, 'stats_cdf_exponential' => 2, 'stats_cdf_f' => 2, 'stats_cdf_gamma' => 2, 'stats_cdf_laplace' => 2, 'stats_cdf_logistic' => 2, 'stats_cdf_negative_binomial' => 2, 'stats_cdf_noncentral_chisquare' => 2, 'stats_cdf_noncentral_f' => 2, 'stats_cdf_noncentral_t' => 2, + 'stats_cdf_normal' => 2, 'stats_cdf_poisson' => 2, 'stats_cdf_t' => 2, 'stats_cdf_uniform' => 2, 'stats_cdf_weibull' => 2, 'stats_covariance' => 2, 'stats_dens_beta' => 2, 'stats_dens_cauchy' => 2, 'stats_dens_chisquare' => 2, 'stats_dens_exponential' => 2, 'stats_dens_f' => 2, 'stats_dens_gamma' => 2, 'stats_dens_laplace' => 2, 'stats_dens_logistic' => 2, 'stats_dens_normal' => 2, 'stats_dens_pmf_binomial' => 2, 'stats_dens_pmf_hypergeometric' => 2, 'stats_dens_pmf_negative_binomial' => 2, 'stats_dens_pmf_poisson' => 2, 'stats_dens_t' => 2, 'stats_dens_uniform' => 2, 'stats_dens_weibull' => 2, 'stats_harmonic_mean' => 2, 'stats_kurtosis' => 2, 'stats_rand_gen_beta' => 2, 'stats_rand_gen_chisquare' => 2, + 'stats_rand_gen_exponential' => 2, 'stats_rand_gen_f' => 2, 'stats_rand_gen_funiform' => 2, 'stats_rand_gen_gamma' => 2, 'stats_rand_gen_ipoisson' => 2, 'stats_rand_gen_iuniform' => 2, 'stats_rand_gen_noncenral_f' => 2, 'stats_rand_gen_noncentral_chisquare' => 2, 'stats_rand_gen_noncentral_t' => 2, 'stats_rand_gen_normal' => 2, 'stats_rand_gen_t' => 2, 'stats_rand_getsd' => 2, 'stats_rand_ibinomial' => 2, 'stats_rand_ibinomial_negative' => 2, 'stats_rand_ignlgi' => 2, 'stats_rand_phrase_to_seeds' => 2, 'stats_rand_ranf' => 2, 'stats_rand_setall' => 2, 'stats_skew' => 2, 'stats_standard_deviation' => 2, 'stats_stat_binomial_coef' => 2, 'stats_stat_correlation' => 2, 'stats_stat_factorial' => 2, 'stats_stat_independent_t' => 2, 'stats_stat_innerproduct' => 2, 'stats_stat_paired_t' => 2, + 'stats_stat_percentile' => 2, 'stats_stat_powersum' => 2, 'stats_variance' => 2, 'str_getcsv' => 2, 'str_ireplace' => 2, 'str_pad' => 2, 'str_repeat' => 2, 'str_replace' => 2, 'str_rot13' => 2, 'str_shuffle' => 2, 'str_split' => 2, 'str_word_count' => 2, 'strcasecmp' => 2, 'strchr' => 2, 'strcmp' => 2, 'strcoll' => 2, 'strcspn' => 2, 'stream_bucket_append' => 2, 'stream_bucket_make_writeable' => 2, 'stream_bucket_new' => 2, 'stream_bucket_prepend' => 2, 'stream_context_create' => 2, 'stream_context_get_default' => 2, 'stream_context_get_options' => 2, 'stream_context_get_params' => 2, 'stream_context_set_default' => 2, + 'stream_context_set_option' => 2, 'stream_context_set_params' => 2, 'stream_copy_to_stream' => 2, 'stream_encoding' => 2, 'stream_filter_append' => 2, 'stream_filter_prepend' => 2, 'stream_filter_register' => 2, 'stream_filter_remove' => 2, 'stream_get_contents' => 2, 'stream_get_filters' => 2, 'stream_get_line' => 2, 'stream_get_meta_data' => 2, 'stream_get_transports' => 2, 'stream_get_wrappers' => 2, 'stream_is_local' => 2, 'stream_notification_callback' => 2, 'stream_register_wrapper' => 2, 'stream_resolve_include_path' => 2, 'stream_select' => 2, 'stream_set_blocking' => 2, 'stream_set_chunk_size' => 2, 'stream_set_read_buffer' => 2, 'stream_set_timeout' => 2, 'stream_set_write_buffer' => 2, 'stream_socket_accept' => 2, 'stream_socket_client' => 2, + 'stream_socket_enable_crypto' => 2, 'stream_socket_get_name' => 2, 'stream_socket_pair' => 2, 'stream_socket_recvfrom' => 2, 'stream_socket_sendto' => 2, 'stream_socket_server' => 2, 'stream_socket_shutdown' => 2, 'stream_supports_lock' => 2, 'stream_wrapper_register' => 2, 'stream_wrapper_restore' => 2, 'stream_wrapper_unregister' => 2, 'strftime' => 2, 'strip_tags' => 2, 'stripcslashes' => 2, 'stripos' => 2, 'stripslashes' => 2, 'stristr' => 2, 'strlen' => 2, 'strnatcasecmp' => 2, 'strnatcmp' => 2, 'strncasecmp' => 2, 'strncmp' => 2, 'strpbrk' => 2, 'strpos' => 2, 'strrchr' => 2, 'strrev' => 2, + 'strripos' => 2, 'strrpos' => 2, 'strspn' => 2, 'strstr' => 2, 'strtok' => 2, 'strtolower' => 2, 'strtotime' => 2, 'strtoupper' => 2, 'strtr' => 2, 'strval' => 2, 'substr' => 2, 'substr_compare' => 2, 'substr_count' => 2, 'substr_replac' => 2, 'substr_replace' => 2, 'svn_add' => 2, 'svn_auth_get_parameter' => 2, 'svn_auth_set_parameter' => 2, 'svn_cat' => 2, 'svn_checkout' => 2, 'svn_cleanup' => 2, 'svn_client_version' => 2, 'svn_commit' => 2, 'svn_diff' => 2, 'svn_export' => 2, 'svn_fs_abort_txn' => 2, + 'svn_fs_apply_text' => 2, 'svn_fs_begin_txn2' => 2, 'svn_fs_change_node_prop' => 2, 'svn_fs_check_path' => 2, 'svn_fs_contents_changed' => 2, 'svn_fs_copy' => 2, 'svn_fs_delete' => 2, 'svn_fs_dir_entries' => 2, 'svn_fs_file_contents' => 2, 'svn_fs_file_length' => 2, 'svn_fs_is_dir' => 2, 'svn_fs_is_file' => 2, 'svn_fs_make_dir' => 2, 'svn_fs_make_file' => 2, 'svn_fs_node_created_rev' => 2, 'svn_fs_node_prop' => 2, 'svn_fs_props_changed' => 2, 'svn_fs_revision_prop' => 2, 'svn_fs_revision_root' => 2, 'svn_fs_txn_root' => 2, 'svn_fs_youngest_rev' => 2, 'svn_import' => 2, 'svn_info' => 2, 'svn_log' => 2, 'svn_ls' => 2, 'svn_repos_create' => 2, + 'svn_repos_fs' => 2, 'svn_repos_fs_begin_txn_for_commit' => 2, 'svn_repos_fs_commit_txn' => 2, 'svn_repos_hotcopy' => 2, 'svn_repos_open' => 2, 'svn_repos_recover' => 2, 'svn_status' => 2, 'svn_update' => 2, 'sybase_affected_rows' => 2, 'sybase_close' => 2, 'sybase_connect' => 2, 'sybase_data_seek' => 2, 'sybase_fetch_array' => 2, 'sybase_fetch_field' => 2, 'sybase_fetch_object' => 2, 'sybase_fetch_row' => 2, 'sybase_field_seek' => 2, 'sybase_free_result' => 2, 'sybase_num_fields' => 2, 'sybase_num_rows' => 2, 'sybase_pconnect' => 2, 'sybase_query' => 2, 'sybase_result' => 2, 'sybase_select_db' => 2, 'symlink' => 2, 'syntax' => 2, + 'sys_get_temp_dir' => 2, 'syslog' => 2, 'system' => 2, 'tan' => 2, 'tanh' => 2, 'tempnam' => 2, 'textdomain' => 2, 'thread_get' => 2, 'thread_include' => 2, 'thread_lock' => 2, 'thread_lock_try' => 2, 'thread_mutex_destroy' => 2, 'thread_mutex_init' => 2, 'thread_set' => 2, 'thread_start' => 2, 'thread_unlock' => 2, 'tidy_access_count' => 2, 'tidy_clean_repair' => 2, 'tidy_config_count' => 2, 'tidy_diagnose' => 2, 'tidy_error_count' => 2, 'tidy_get_body' => 2, 'tidy_get_config' => 2, 'tidy_get_error_buffer' => 2, 'tidy_get_head' => 2, 'tidy_get_html' => 2, + 'tidy_get_html_ver' => 2, 'tidy_get_output' => 2, 'tidy_get_release' => 2, 'tidy_get_root' => 2, 'tidy_get_status' => 2, 'tidy_getopt' => 2, 'tidy_is_xhtml' => 2, 'tidy_is_xml' => 2, 'tidy_parse_file' => 2, 'tidy_parse_string' => 2, 'tidy_repair_file' => 2, 'tidy_repair_string' => 2, 'tidy_warning_count' => 2, 'time' => 2, 'time_nanosleep' => 2, 'time_sleep_until' => 2, 'timezone_abbreviations_list' => 2, 'timezone_identifiers_list' => 2, 'timezone_location_get' => 2, 'timezone_name_from_abbr' => 2, 'timezone_name_get' => 2, 'timezone_offset_get' => 2, 'timezone_open' => 2, 'timezone_transitions_get' => 2, 'timezone_version_get' => 2, 'tmpfile' => 2, + 'token_get_all' => 2, 'token_name' => 2, 'touch' => 2, 'trait_exists' => 2, 'transliterate' => 2, 'transliterate_filters_get' => 2, 'trigger_error' => 2, 'trim' => 2, 'uasort' => 2, 'ucfirst' => 2, 'ucwords' => 2, 'uksort' => 2, 'umask' => 2, 'uniqid' => 2, 'unixtojd' => 2, 'unlink' => 2, 'unpack' => 2, 'unregister_tick_function' => 2, 'unserialize' => 2, 'urldecode' => 2, 'urlencode' => 2, 'use_soap_error_handler' => 2, 'user_error' => 2, 'usleep' => 2, 'usort' => 2, 'utf8_decode' => 2, + 'utf8_encode' => 2, 'var_dump' => 2, 'var_export' => 2, 'variant_abs' => 2, 'variant_add' => 2, 'variant_and' => 2, 'variant_cast' => 2, 'variant_cat' => 2, 'variant_cmp' => 2, 'variant_date_from_timestamp' => 2, 'variant_date_to_timestamp' => 2, 'variant_div' => 2, 'variant_eqv' => 2, 'variant_fix' => 2, 'variant_get_type' => 2, 'variant_idiv' => 2, 'variant_imp' => 2, 'variant_int' => 2, 'variant_mod' => 2, 'variant_mul' => 2, 'variant_neg' => 2, 'variant_not' => 2, 'variant_or' => 2, 'variant_pow' => 2, 'variant_round' => 2, 'variant_set' => 2, + 'variant_set_type' => 2, 'variant_sub' => 2, 'variant_xor' => 2, 'version_compare' => 2, 'vfprintf' => 2, 'virtual' => 2, 'vm_addalias' => 2, 'vm_adduser' => 2, 'vm_delalias' => 2, 'vm_deluser' => 2, 'vm_passwd' => 2, 'vprintf' => 2, 'vsprintf' => 2, 'wddx_add_vars' => 2, 'wddx_deserialize' => 2, 'wddx_packet_end' => 2, 'wddx_packet_start' => 2, 'wddx_serialize_value' => 2, 'wddx_serialize_vars' => 2, 'win32_create_service' => 2, 'win32_delete_service' => 2, 'win32_get_last_control_message' => 2, 'win32_ps_list_procs' => 2, 'win32_ps_stat_mem' => 2, 'win32_ps_stat_proc' => 2, 'win32_query_service_status' => 2, + 'win32_scheduler_delete_task' => 2, 'win32_scheduler_enum_tasks' => 2, 'win32_scheduler_get_task_info' => 2, 'win32_scheduler_run' => 2, 'win32_scheduler_set_task_info' => 2, 'win32_set_service_status' => 2, 'win32_start_service' => 2, 'win32_start_service_ctrl_dispatcher' => 2, 'win32_stop_service' => 2, 'win_beep' => 2, 'win_browse_file' => 2, 'win_browse_folder' => 2, 'win_create_link' => 2, 'win_message_box' => 2, 'win_play_wav' => 2, 'win_shell_execute' => 2, 'wordwrap' => 2, 'xml_error_string' => 2, 'xml_get_current_byte_index' => 2, 'xml_get_current_column_number' => 2, 'xml_get_current_line_number' => 2, 'xml_get_error_code' => 2, 'xml_parse' => 2, 'xml_parse_into_struct' => 2, 'xml_parser_create' => 2, 'xml_parser_create_ns' => 2, + 'xml_parser_free' => 2, 'xml_parser_get_option' => 2, 'xml_parser_set_option' => 2, 'xml_set_character_data_handler' => 2, 'xml_set_default_handler' => 2, 'xml_set_element_handler' => 2, 'xml_set_end_namespace_decl_handler' => 2, 'xml_set_external_entity_ref_handler' => 2, 'xml_set_notation_decl_handler' => 2, 'xml_set_object' => 2, 'xml_set_processing_instruction_handler' => 2, 'xml_set_start_namespace_decl_handler' => 2, 'xml_set_unparsed_entity_decl_handler' => 2, 'xmldoc' => 2, 'xmldocfile' => 2, 'xmlrpc_decode' => 2, 'xmlrpc_decode_request' => 2, 'xmlrpc_encode' => 2, 'xmlrpc_encode_request' => 2, 'xmlrpc_get_type' => 2, 'xmlrpc_is_fault' => 2, 'xmlrpc_parse_method_descriptions' => 2, 'xmlrpc_server_add_introspection_data' => 2, 'xmlrpc_server_call_method' => 2, 'xmlrpc_server_create' => 2, 'xmlrpc_server_destroy' => 2, + 'xmlrpc_server_register_introspection_callback' => 2, 'xmlrpc_server_register_method' => 2, 'xmlrpc_set_type' => 2, 'xmltree' => 2, 'xmlwriter_end_attribute' => 2, 'xmlwriter_end_cdata' => 2, 'xmlwriter_end_comment' => 2, 'xmlwriter_end_document' => 2, 'xmlwriter_end_dtd' => 2, 'xmlwriter_end_dtd_attlist' => 2, 'xmlwriter_end_dtd_element' => 2, 'xmlwriter_end_dtd_entity' => 2, 'xmlwriter_end_element' => 2, 'xmlwriter_end_pi' => 2, 'xmlwriter_flush' => 2, 'xmlwriter_full_end_element' => 2, 'xmlwriter_open_memory' => 2, 'xmlwriter_open_uri' => 2, 'xmlwriter_output_memory' => 2, 'xmlwriter_set_indent' => 2, 'xmlwriter_set_indent_string' => 2, 'xmlwriter_start_attribute' => 2, 'xmlwriter_start_attribute_ns' => 2, 'xmlwriter_start_cdata' => 2, 'xmlwriter_start_comment' => 2, 'xmlwriter_start_document' => 2, + 'xmlwriter_start_dtd' => 2, 'xmlwriter_start_dtd_attlist' => 2, 'xmlwriter_start_dtd_element' => 2, 'xmlwriter_start_dtd_entity' => 2, 'xmlwriter_start_element' => 2, 'xmlwriter_start_element_ns' => 2, 'xmlwriter_start_pi' => 2, 'xmlwriter_text' => 2, 'xmlwriter_write_attribute' => 2, 'xmlwriter_write_attribute_ns' => 2, 'xmlwriter_write_cdata' => 2, 'xmlwriter_write_comment' => 2, 'xmlwriter_write_dtd' => 2, 'xmlwriter_write_dtd_attlist' => 2, 'xmlwriter_write_dtd_element' => 2, 'xmlwriter_write_dtd_entity' => 2, 'xmlwriter_write_element' => 2, 'xmlwriter_write_element_ns' => 2, 'xmlwriter_write_pi' => 2, 'xmlwriter_write_raw' => 2, 'xpath_eval' => 2, 'xpath_eval_expression' => 2, 'xpath_new_context' => 2, 'xpath_register_ns' => 2, 'xpath_register_ns_auto' => 2, 'xptr_eval' => 2, + 'xptr_new_context' => 2, 'yp_all' => 2, 'yp_cat' => 2, 'yp_err_string' => 2, 'yp_errno' => 2, 'yp_first' => 2, 'yp_get_default_domain' => 2, 'yp_master' => 2, 'yp_match' => 2, 'yp_next' => 2, 'yp_order' => 2, 'zend_current_obfuscation_level' => 2, 'zend_get_cfg_var' => 2, 'zend_get_id' => 2, 'zend_loader_current_file' => 2, 'zend_loader_enabled' => 2, 'zend_loader_file_encoded' => 2, 'zend_loader_file_licensed' => 2, 'zend_loader_install_license' => 2, 'zend_loader_version' => 2, 'zend_logo_guid' => 2, 'zend_match_hostmasks' => 2, 'zend_obfuscate_class_name' => 2, 'zend_obfuscate_function_name' => 2, 'zend_optimizer_version' => 2, 'zend_runtime_obfuscate' => 2, + 'zend_version' => 2, 'zip_close' => 2, 'zip_entry_close' => 2, 'zip_entry_compressedsize' => 2, 'zip_entry_compressionmethod' => 2, 'zip_entry_filesize' => 2, 'zip_entry_name' => 2, 'zip_entry_open' => 2, 'zip_entry_read' => 2, 'zip_open' => 2, 'zip_read' => 2, 'zlib_decode' => 2, 'zlib_encode' => 2, 'zlib_get_coding_type' => 2 + ), 2 => false + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '$', 1 => '\'', 2 => '"', 4 => '_', 7 => "\n", 8 => "\t", 9 => '//', 10 => '/*', 11 => '#', 12 => '?>', 13 => ' ' ' ' "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '?>' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state VAR. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => '$', 2 => '{', 3 => '}' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state VAR_STR. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '}' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\s+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 1 => '\\\\', 2 => '\\"', 3 => '$', 4 => '{$', 5 => "\n", 6 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if ($delimiters[6] === $letter) { + return array(6, $delimiters[6], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEREDOC. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '\\$', 3 => '$', 4 => '{$' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEREDOC_END. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if (preg_match('~\\w+;\\n~A', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter10($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 1 => '\\\\', 2 => '\\\'', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NOWDOC. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter11($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NOWDOC_END. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter12($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if (preg_match('~\\w+;\\n~A', $text, $matches, 0, $textPos)) { + return array(0, $matches[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter13($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'e', 1 => 'E', 2 => 'x', 3 => 'b' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(4, $matches[0], $buffer); + } + return array(5, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state EXPONENT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter14($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '+', 1 => '-' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^\\D+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter15($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Python.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Python.php new file mode 100644 index 00000000000..2d161639a8d --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Python.php @@ -0,0 +1,711 @@ +language = 'Python'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 1, 1 => -1 + ), 1 => array( + 0 => 1, 1 => -1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 3, 1 => 1 + ), 4 => array( + 0 => 4, 1 => 1 + ), 5 => array( + 0 => 5, 1 => 1 + ), 6 => array( + 0 => 6, 1 => 1 + ), 7 => array( + 0 => 7, 1 => 1 + ), 8 => array( + 0 => 7, 1 => 1 + ), 9 => array( + 0 => 0, 1 => 1 + ), 10 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 11, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 2, 1 => 1 + ), 3 => array( + 0 => 2, 1 => 1 + ), 4 => array( + 0 => 2, 1 => 1 + ) + ), 3 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 3, 1 => 1 + ), 2 => array( + 0 => 3, 1 => 1 + ), 3 => array( + 0 => 3, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 4, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 5, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ), 4 => array( + 0 => 5, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 11, 1 => -1 + ), 1 => array( + 0 => 6, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 11, 1 => 0 + ), 2 => array( + 0 => 11, 1 => 0 + ), 3 => array( + 0 => 11, 1 => 0 + ), 4 => array( + 0 => 11, 1 => 0 + ), 5 => array( + 0 => 9, 1 => 1 + ), 6 => array( + 0 => 9, 1 => 1 + ), 7 => array( + 0 => 9, 1 => 1 + ), 8 => array( + 0 => 10, 1 => 1 + ), 9 => array( + 0 => 10, 1 => 1 + ), 10 => array( + 0 => 11, 1 => -1 + ) + ), 8 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 11, 1 => 0 + ), 2 => array( + 0 => 9, 1 => 1 + ), 3 => array( + 0 => 9, 1 => 1 + ), 4 => array( + 0 => 9, 1 => 1 + ), 5 => array( + 0 => 11, 1 => -1 + ) + ), 9 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 11, 1 => 0 + ), 2 => array( + 0 => 11, 1 => -1 + ) + ), 10 => array( + 0 => array( + 0 => 11, 1 => 0 + ), 1 => array( + 0 => 11, 1 => 0 + ), 2 => array( + 0 => 11, 1 => -1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 11; + $this->quitState = 12; + $this->flags = array( + 0 => 0, 1 => 5, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 0, 9 => 0, 10 => 4 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => NULL, 2 => 'py-docstring', 3 => 'py-docstring', 4 => 'py-quote', 5 => 'py-quote', 6 => 'py-comment', 7 => 'py-num', 8 => 'py-num', 9 => 'py-num', 10 => 'py-num' + ); + $this->keywords = array( + 0 => 'py-keyword', 1 => array( + 'and' => 1, 'as' => 1, 'assert' => 1, 'break' => 1, 'class' => 1, 'continue' => 1, 'def' => 1, 'del' => 1, 'elif' => 1, 'else' => 1, 'except' => 1, 'exec' => 1, 'finally' => 1, 'for' => 1, 'from' => 1, 'global' => 1, 'if' => 1, 'import' => 1, 'in' => 1, 'is' => 1, 'lambda' => 1, 'not' => 1, 'or' => 1, 'pass' => 1, 'print' => 1, 'raise' => 1, + 'return' => 1, 'try' => 1, 'while' => 1, 'with' => 1, 'yield' => 1, 'abs' => 2, 'all' => 2, 'any' => 2, 'apply' => 2, 'basestring' => 2, 'bool' => 2, 'buffer' => 2, 'callable' => 2, 'chr' => 2, 'classmethod' => 2, 'cmp' => 2, 'coerce' => 2, 'compile' => 2, 'complex' => 2, 'delattr' => 2, 'dict' => 2, 'dir' => 2, 'divmod' => 2, 'enumerate' => 2, 'eval' => 2, 'execfile' => 2, + 'file' => 2, 'filter' => 2, 'float' => 2, 'frozenset' => 2, 'getattr' => 2, 'globals' => 2, 'hasattr' => 2, 'hash' => 2, 'hex' => 2, 'id' => 2, 'input' => 2, 'int' => 2, 'intern' => 2, 'isinstance' => 2, 'issubclass' => 2, 'iter' => 2, 'len' => 2, 'list' => 2, 'locals' => 2, 'long' => 2, 'map' => 2, 'max' => 2, 'min' => 2, 'object' => 2, 'oct' => 2, 'open' => 2, + 'ord' => 2, 'pow' => 2, 'property' => 2, 'range' => 2, 'raw_input' => 2, 'reduce' => 2, 'reload' => 2, 'repr' => 2, 'reversed' => 2, 'round' => 2, 'set' => 2, 'setattr' => 2, 'slice' => 2, 'sorted' => 2, 'staticmethod' => 2, 'str' => 2, 'sum' => 2, 'super' => 2, 'tuple' => 2, 'type' => 2, 'unichr' => 2, 'unicode' => 2, 'vars' => 2, 'xrange' => 2, 'zip' => 2, 'ArithmeticError' => 3, + 'AssertionError' => 3, 'AttributeError' => 3, 'BaseException' => 3, 'DeprecationWarning' => 3, 'EOFError' => 3, 'Ellipsis' => 3, 'EnvironmentError' => 3, 'Exception' => 3, 'FloatingPointError' => 3, 'FutureWarning' => 3, 'GeneratorExit' => 3, 'IOError' => 3, 'ImportError' => 3, 'ImportWarning' => 3, 'IndentationError' => 3, 'IndexError' => 3, 'KeyError' => 3, 'KeyboardInterrupt' => 3, 'LookupError' => 3, 'MemoryError' => 3, 'NameError' => 3, 'NotImplemented' => 3, 'NotImplementedError' => 3, 'OSError' => 3, 'OverflowError' => 3, 'OverflowWarning' => 3, + 'PendingDeprecationWarning' => 3, 'ReferenceError' => 3, 'RuntimeError' => 3, 'RuntimeWarning' => 3, 'StandardError' => 3, 'StopIteration' => 3, 'SyntaxError' => 3, 'SyntaxWarning' => 3, 'SystemError' => 3, 'SystemExit' => 3, 'TabError' => 3, 'TypeError' => 3, 'UnboundLocalError' => 3, 'UnicodeDecodeError' => 3, 'UnicodeEncodeError' => 3, 'UnicodeError' => 3, 'UnicodeTranslateError' => 3, 'UnicodeWarning' => 3, 'UserWarning' => 3, 'ValueError' => 3, 'Warning' => 3, 'WindowsError' => 3, 'ZeroDivisionError' => 3, 'BufferType' => 3, 'BuiltinFunctionType' => 3, 'BuiltinMethodType' => 3, + 'ClassType' => 3, 'CodeType' => 3, 'ComplexType' => 3, 'DictProxyType' => 3, 'DictType' => 3, 'DictionaryType' => 3, 'EllipsisType' => 3, 'FileType' => 3, 'FloatType' => 3, 'FrameType' => 3, 'FunctionType' => 3, 'GeneratorType' => 3, 'InstanceType' => 3, 'IntType' => 3, 'LambdaType' => 3, 'ListType' => 3, 'LongType' => 3, 'MethodType' => 3, 'ModuleType' => 3, 'NoneType' => 3, 'ObjectType' => 3, 'SliceType' => 3, 'StringType' => 3, 'StringTypes' => 3, 'TracebackType' => 3, 'TupleType' => 3, + 'TypeType' => 3, 'UnboundMethodType' => 3, 'UnicodeType' => 3, 'XRangeType' => 3, 'False' => 3, 'None' => 3, 'True' => 3, '__abs__' => 3, '__add__' => 3, '__all__' => 3, '__author__' => 3, '__bases__' => 3, '__builtins__' => 3, '__call__' => 3, '__class__' => 3, '__cmp__' => 3, '__coerce__' => 3, '__contains__' => 3, '__debug__' => 3, '__del__' => 3, '__delattr__' => 3, '__delitem__' => 3, '__delslice__' => 3, '__dict__' => 3, '__div__' => 3, '__divmod__' => 3, + '__doc__' => 3, '__eq__' => 3, '__file__' => 3, '__float__' => 3, '__floordiv__' => 3, '__future__' => 3, '__ge__' => 3, '__getattr__' => 3, '__getattribute__' => 3, '__getitem__' => 3, '__getslice__' => 3, '__gt__' => 3, '__hash__' => 3, '__hex__' => 3, '__iadd__' => 3, '__import__' => 3, '__imul__' => 3, '__init__' => 3, '__int__' => 3, '__invert__' => 3, '__iter__' => 3, '__le__' => 3, '__len__' => 3, '__long__' => 3, '__lshift__' => 3, '__lt__' => 3, + '__members__' => 3, '__metaclass__' => 3, '__mod__' => 3, '__mro__' => 3, '__mul__' => 3, '__name__' => 3, '__ne__' => 3, '__neg__' => 3, '__new__' => 3, '__nonzero__' => 3, '__oct__' => 3, '__or__' => 3, '__path__' => 3, '__pos__' => 3, '__pow__' => 3, '__radd__' => 3, '__rdiv__' => 3, '__rdivmod__' => 3, '__reduce__' => 3, '__repr__' => 3, '__rfloordiv__' => 3, '__rlshift__' => 3, '__rmod__' => 3, '__rmul__' => 3, '__ror__' => 3, '__rpow__' => 3, + '__rrshift__' => 3, '__rsub__' => 3, '__rtruediv__' => 3, '__rxor__' => 3, '__setattr__' => 3, '__setitem__' => 3, '__setslice__' => 3, '__self__' => 3, '__slots__' => 3, '__str__' => 3, '__sub__' => 3, '__truediv__' => 3, '__version__' => 3, '__xor__' => 3 + ), 2 => true + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => '_', 2 => '\'\'\'', 3 => '"""', 4 => '\'', 5 => '"', 6 => '#', 9 => "\n", 10 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^[a-z]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if ($delimiters[6] === $letter) { + return array(6, $delimiters[6], $buffer); + } + if (preg_match('~^\\d+~', $part, $matches)) { + return array(7, $matches[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(8, $matches[0], $buffer); + } + if ($delimiters[9] === $letter) { + return array(9, $delimiters[9], $buffer); + } + if ($delimiters[10] === $letter) { + return array(10, $delimiters[10], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state KEYWORD. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state DOCSTRING1. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'\'\'', 1 => '\\\\', 2 => '\\\'\'\'', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state DOCSTRING2. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"""', 1 => '\\\\', 2 => '\\"""', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 1 => '\\\\', 2 => '\\\'', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 1 => '\\\\', 2 => '\\"', 3 => "\n", 4 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 1 => 'l', 2 => 'L', 3 => 'j', 4 => 'J', 5 => 'e-', 6 => 'e+', 7 => 'e', 8 => 'x', 9 => 'X' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + if (0 === strpos($part, $delimiters[5])) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + if ($delimiters[7] === $letter) { + return array(7, $delimiters[7], $buffer); + } + if ($delimiters[8] === $letter) { + return array(8, $delimiters[8], $buffer); + } + if ($delimiters[9] === $letter) { + return array(9, $delimiters[9], $buffer); + } + return array(10, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state FRACTION. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'j', 1 => 'J', 2 => 'e-', 3 => 'e+', 4 => 'e' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + return array(5, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state EXPONENT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'j', 1 => 'J' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^\\D+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter10($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'L', 1 => 'l' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Sql.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Sql.php new file mode 100644 index 00000000000..2345dfec5ab --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Sql.php @@ -0,0 +1,594 @@ +language = 'Sql'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 1, 1 => -1 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 7, 1 => 1 + ), 3 => array( + 0 => 2, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 3, 1 => 1 + ), 6 => array( + 0 => 3, 1 => 1 + ), 7 => array( + 0 => 4, 1 => 1 + ), 8 => array( + 0 => 5, 1 => 1 + ), 9 => array( + 0 => 6, 1 => 1 + ), 10 => array( + 0 => 0, 1 => 1 + ), 11 => array( + 0 => 0, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 10, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 2, 1 => 1 + ), 1 => array( + 0 => 2, 1 => 1 + ), 2 => array( + 0 => 10, 1 => 0 + ) + ), 3 => array( + 0 => array( + 0 => 10, 1 => -1 + ), 1 => array( + 0 => 3, 1 => 1 + ) + ), 4 => array( + 0 => array( + 0 => 10, 1 => 0 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ) + ), 5 => array( + 0 => array( + 0 => 10, 1 => 0 + ), 1 => array( + 0 => 5, 1 => 1 + ), 2 => array( + 0 => 5, 1 => 1 + ), 3 => array( + 0 => 5, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 10, 1 => 0 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 6, 1 => 1 + ), 3 => array( + 0 => 6, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 7, 1 => 1 + ), 2 => array( + 0 => 10, 1 => -1 + ) + ), 8 => array( + 0 => array( + 0 => 10, 1 => -1 + ) + ), 9 => array( + 0 => array( + 0 => 9, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 9, 1 => 1 + ), 3 => array( + 0 => 9, 1 => 1 + ), 4 => array( + 0 => 9, 1 => 1 + ) + ) + ); + $this->initialState = 0; + $this->returnState = 10; + $this->quitState = 11; + $this->flags = array( + 0 => 1, 1 => 5, 2 => 4, 3 => 4, 4 => 4, 5 => 4, 6 => 4, 7 => 4, 8 => 0, 9 => 4 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => NULL, 2 => 'sql-comment', 3 => 'sql-comment', 4 => 'sql-value', 5 => 'sql-value', 6 => 'sql-value', 7 => 'sql-num', 8 => 'sql-num', 9 => 'sql-option' + ); + $this->keywords = array( + 0 => 'sql-keyword', 1 => array( + 'a' => 1, 'abs' => 2, 'acos' => 2, 'add' => 1, 'add_months' => 1, 'after' => 1, 'all' => 1, 'alter' => 1, 'an' => 1, 'and' => 1, 'any' => 1, 'array' => 1, 'as' => 1, 'asc' => 1, 'ascii' => 2, 'asin' => 2, 'atan' => 2, 'atan2' => 2, 'avg' => 2, 'before' => 1, 'begin' => 1, 'between' => 1, 'bigint' => 3, 'binary' => 1, 'bind' => 1, 'binding' => 1, + 'bit' => 1, 'blob' => 3, 'boolean' => 3, 'by' => 1, 'call' => 1, 'cascade' => 1, 'case' => 1, 'cast' => 1, 'ceiling' => 2, 'char' => 3, 'char_length' => 2, 'character' => 2, 'character_length' => 2, 'chartorowid' => 1, 'check' => 1, 'chr' => 1, 'cleanup' => 1, 'close' => 1, 'clustered' => 1, 'coalesce' => 1, 'colgroup' => 1, 'collate' => 1, 'commit' => 1, 'complex' => 1, 'compress' => 1, 'concat' => 2, + 'connect' => 1, 'constraint' => 1, 'contains' => 1, 'continue' => 1, 'convert' => 1, 'cos' => 2, 'count' => 2, 'create' => 1, 'cross' => 1, 'curdate' => 2, 'current' => 1, 'cursor' => 1, 'curtime' => 2, 'cvar' => 1, 'database' => 1, 'datapages' => 1, 'date' => 2, 'dayname' => 2, 'dayofmonth' => 2, 'dayofweek' => 2, 'dayofyear' => 2, 'db_name' => 1, 'dba' => 1, 'dec' => 3, 'decimal' => 3, 'declaration' => 1, + 'declare' => 1, 'decode' => 2, 'default' => 1, 'definition' => 1, 'degrees' => 1, 'delete' => 1, 'desc' => 1, 'describe' => 1, 'descriptor' => 1, 'dhtype' => 1, 'difference' => 1, 'distinct' => 1, 'double' => 3, 'drop' => 1, 'each' => 1, 'else' => 1, 'end' => 1, 'escape' => 1, 'exclusive' => 1, 'exec' => 1, 'execute' => 1, 'exists' => 1, 'exit' => 1, 'exp' => 2, 'explicit' => 1, 'extent' => 1, + 'fetch' => 1, 'field file' => 1, 'float' => 3, 'floor' => 2, 'for' => 1, 'foreign' => 1, 'found' => 1, 'from' => 1, 'full' => 1, 'go' => 1, 'goto' => 1, 'grant' => 1, 'greatest' => 2, 'group' => 1, 'hash' => 1, 'having' => 1, 'hour' => 1, 'identified' => 1, 'ifnull' => 2, 'immediate' => 1, 'in' => 1, 'index' => 1, 'indexpages' => 1, 'indicator' => 1, 'initcap' => 1, 'inner' => 1, + 'inout' => 1, 'input' => 1, 'insert' => 1, 'instr' => 1, 'int' => 3, 'integer' => 3, 'interface' => 1, 'intersect' => 1, 'into' => 1, 'is' => 1, 'isnull' => 2, 'java_object' => 3, 'join' => 1, 'key' => 1, 'last_day' => 2, 'lcase' => 2, 'least' => 2, 'left' => 2, 'length' => 2, 'like' => 1, 'link' => 1, 'list' => 1, 'locate' => 1, 'lock' => 1, 'log' => 2, 'log10' => 2, + 'long' => 1, 'longblob' => 3, 'longtext' => 3, 'longvarbinary' => 3, 'longvarchar' => 3, 'lower' => 1, 'lpad' => 1, 'ltrim' => 2, 'lvarbinary' => 1, 'lvarchar' => 1, 'main' => 1, 'max' => 2, 'mediumint' => 3, 'metadata_only' => 1, 'min' => 2, 'minus' => 2, 'minute' => 2, 'mod' => 2, 'mode' => 1, 'modify' => 1, 'money' => 1, 'month' => 2, 'monthname' => 2, 'months_between' => 2, 'name' => 1, 'national' => 1, + 'natural' => 1, 'nchar' => 1, 'newrow' => 1, 'next_day' => 1, 'nocompress' => 1, 'not' => 1, 'now' => 1, 'nowait' => 1, 'null' => 1, 'nullif' => 1, 'nullvalue' => 1, 'number' => 1, 'numeric' => 1, 'nvl' => 1, 'object_id' => 1, 'odbc_convert' => 1, 'odbcinfo' => 1, 'of' => 1, 'oldrow' => 1, 'on' => 1, 'open' => 1, 'option' => 1, 'or' => 1, 'order' => 1, 'out' => 1, 'outer' => 1, + 'output' => 1, 'pctfree' => 1, 'pi' => 1, 'power' => 1, 'precision' => 1, 'prefix' => 1, 'prepare' => 1, 'primary' => 1, 'privileges' => 1, 'procedure' => 1, 'public' => 1, 'quarter' => 2, 'radians' => 2, 'rand' => 2, 'range' => 2, 'raw' => 1, 'real' => 3, 'record' => 1, 'references' => 1, 'referencing' => 1, 'rename' => 1, 'repeat' => 2, 'replace' => 1, 'resource' => 1, 'restrict' => 1, 'result' => 1, + 'return' => 2, 'revoke' => 2, 'right' => 2, 'rollback' => 1, 'row' => 2, 'rowid' => 2, 'rowidtochar' => 2, 'rownum' => 2, 'rpad' => 2, 'rtrim' => 2, 'searched_case' => 1, 'second' => 1, 'section' => 1, 'select' => 1, 'service' => 1, 'set' => 1, 'share' => 1, 'short' => 1, 'sign' => 1, 'simple_case' => 1, 'sin' => 2, 'size' => 2, 'smallint' => 3, 'some' => 1, 'soundex' => 1, 'space' => 1, + 'sql' => 1, 'sql_bigint' => 3, 'sql_binary' => 3, 'sql_bit' => 3, 'sql_char' => 3, 'sql_date' => 3, 'sql_decimal' => 3, 'sql_double' => 3, 'sql_float' => 1, 'sql_integer' => 3, 'sql_longvarbinary' => 3, 'sql_longvarchar' => 3, 'sql_numeric' => 3, 'sql_real' => 3, 'sql_smallint' => 3, 'sql_time' => 3, 'sql_timestamp' => 1, 'sql_tinyint' => 3, 'sql_tsi_day' => 3, 'sql_tsi_frac_second' => 3, 'sql_tsi_hour' => 3, 'sql_tsi_minute' => 3, 'sql_tsi_month' => 3, 'sql_tsi_quarter' => 3, 'sql_tsi_second' => 3, 'sql_tsi_week' => 3, + 'sql_tsi_year' => 3, 'sql_varbinary' => 3, 'sql_varchar' => 3, 'sqlerror' => 1, 'sqlwarning' => 1, 'sqrt' => 1, 'start' => 1, 'statement' => 1, 'statistics' => 1, 'stop' => 1, 'storage_attributes' => 1, 'storage_manager' => 1, 'store_in_progress' => 1, 'string' => 3, 'substr' => 2, 'substring' => 2, 'suffix' => 2, 'sum' => 2, 'suser_name' => 2, 'synonym' => 2, 'sysdate' => 2, 'systime' => 2, 'systimestamp' => 2, 'table' => 1, 'tan' => 2, 'text' => 3, + 'then' => 1, 'time' => 2, 'timeout' => 2, 'timestamp' => 3, 'timestampadd' => 2, 'timestampdiff' => 2, 'tinyint' => 3, 'to' => 2, 'to_char' => 2, 'to_date' => 2, 'to_number' => 2, 'to_time' => 2, 'to_timestamp' => 2, 'top' => 1, 'tpe' => 1, 'tran' => 1, 'transaction' => 1, 'translate' => 1, 'trigger' => 1, 'type' => 1, 'ucase' => 1, 'uid' => 1, 'union' => 1, 'unique' => 1, 'unsigned' => 1, 'update' => 1, + 'upper' => 1, 'user' => 1, 'user_id' => 1, 'user_name' => 1, 'using' => 1, 'uuid' => 1, 'values' => 1, 'varbinary' => 1, 'varchar' => 3, 'variables' => 1, 'varying' => 1, 'version' => 1, 'view' => 1, 'week' => 2, 'when' => 1, 'whenever' => 1, 'where' => 1, 'with' => 1, 'work' => 1, 'year' => 1 + ), 2 => false + ); + + } + + /** + * Finds a delimiter for state OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 3 => '/*', 4 => '//', 5 => '#', 6 => '--', 7 => '"', 8 => '\'', 9 => '`', 10 => "\n", 11 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (preg_match('~^[a-z]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + if (preg_match('~^\\d+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(2, $matches[0], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + if ($delimiters[5] === $letter) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + if ($delimiters[7] === $letter) { + return array(7, $delimiters[7], $buffer); + } + if ($delimiters[8] === $letter) { + return array(8, $delimiters[8], $buffer); + } + if ($delimiters[9] === $letter) { + return array(9, $delimiters[9], $buffer); + } + if ($delimiters[10] === $letter) { + return array(10, $delimiters[10], $buffer); + } + if ($delimiters[11] === $letter) { + return array(11, $delimiters[11], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state FUNCTION. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^\\W+~', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_BLOCK. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t", 2 => '*/' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state COMMENT_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '"', 1 => '\\"', 2 => "\n", 3 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\'', 1 => '\\\'', 2 => "\n", 3 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state QUOTE_BACK_APOSTROPHE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '`', 1 => '\\`', 2 => "\n", 3 => "\t" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state NUMBER. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'x' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\.\\d+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + return array(2, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEXA. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (preg_match('~^[^a-f\\d]+~i', $part, $matches)) { + return array(0, $matches[0], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state OPTION. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'BLOB', 1 => 'TEXT', 2 => 'INTEGER', 3 => 'CHAR', 4 => 'DATE' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $text[$textPos]; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cache/Texy.php b/apigen/libs/FSHL/FSHL/Lexer/Cache/Texy.php new file mode 100644 index 00000000000..69feb42bc4d --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cache/Texy.php @@ -0,0 +1,972 @@ +language = 'Texy'; + $this->trans = array( + 0 => array( + 0 => array( + 0 => 8, 1 => 1 + ), 1 => array( + 0 => 9, 1 => 1 + ), 2 => array( + 0 => 1, 1 => 1 + ) + ), 1 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 2, 1 => -1 + ) + ), 2 => array( + 0 => array( + 0 => 4, 1 => 1 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 0, 1 => -1 + ) + ), 3 => array( + 0 => array( + 0 => 3, 1 => 1 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 7, 1 => 1 + ), 4 => array( + 0 => 7, 1 => 1 + ), 5 => array( + 0 => 7, 1 => 1 + ), 6 => array( + 0 => 7, 1 => 1 + ), 7 => array( + 0 => 0, 1 => -1 + ) + ), 4 => array( + 0 => array( + 0 => 4, 1 => 1 + ), 1 => array( + 0 => 4, 1 => 1 + ), 2 => array( + 0 => 4, 1 => 1 + ), 3 => array( + 0 => 4, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ), 5 => array( + 0 => 5, 1 => -1 + ) + ), 5 => array( + 0 => array( + 0 => 6, 1 => 1 + ), 1 => array( + 0 => 6, 1 => 1 + ), 2 => array( + 0 => 6, 1 => 1 + ), 3 => array( + 0 => 6, 1 => 1 + ), 4 => array( + 0 => 3, 1 => 1 + ) + ), 6 => array( + 0 => array( + 0 => 3, 1 => 1 + ) + ), 7 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ), 8 => array( + 0 => array( + 0 => 15, 1 => 1 + ), 1 => array( + 0 => 19, 1 => 1 + ), 2 => array( + 0 => 10, 1 => 1 + ), 3 => array( + 0 => 11, 1 => 1 + ), 4 => array( + 0 => 0, 1 => -1 + ) + ), 9 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ), 10 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ), 11 => array( + 0 => array( + 0 => 12, 1 => -1 + ) + ), 12 => array( + 0 => array( + 0 => 13, 1 => 1 + ) + ), 13 => array( + 0 => array( + 0 => 14, 1 => 1 + ), 1 => array( + 0 => 12, 1 => -1 + ) + ), 14 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ), 15 => array( + 0 => array( + 0 => 16, 1 => -1 + ) + ), 16 => array( + 0 => array( + 0 => 17, 1 => 1 + ) + ), 17 => array( + 0 => array( + 0 => 18, 1 => 1 + ), 1 => array( + 0 => 16, 1 => -1 + ) + ), 18 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ), 19 => array( + 0 => array( + 0 => 20, 1 => -1 + ) + ), 20 => array( + 0 => array( + 0 => 21, 1 => 1 + ) + ), 21 => array( + 0 => array( + 0 => 22, 1 => 1 + ), 1 => array( + 0 => 20, 1 => -1 + ) + ), 22 => array( + 0 => array( + 0 => 0, 1 => -1 + ) + ) + ); + $this->initialState = 2; + $this->returnState = 23; + $this->quitState = 24; + $this->flags = array( + 0 => 0, 1 => 0, 2 => 0, 3 => 'texy-err', 4 => 0, 5 => 0, 6 => 0, 7 => 0, 8 => 0, 9 => 0, 10 => 0, 11 => 0, 12 => 0, 13 => 0, 14 => 0, 15 => 0, 16 => 0, 17 => 0, 18 => 0, 19 => 0, 20 => 0, 21 => 0, 22 => 0 + ); + $this->data = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => NULL, 5 => NULL, 6 => NULL, 7 => NULL, 8 => NULL, 9 => NULL, 10 => NULL, 11 => NULL, 12 => NULL, 13 => NULL, 14 => NULL, 15 => NULL, 16 => NULL, 17 => NULL, 18 => NULL, 19 => NULL, 20 => NULL, 21 => NULL, 22 => NULL + ); + $this->classes = array( + 0 => NULL, 1 => NULL, 2 => NULL, 3 => NULL, 4 => 'texy-hlead', 5 => 'texy-hbody', 6 => 'texy-hlead', 7 => 'texy-hr', 8 => 'texy-hr', 9 => 'texy-hr', 10 => 'texy-hr', 11 => 'texy-hr', 12 => 'texy-text', 13 => 'texy-text', 14 => 'texy-hr', 15 => 'texy-hr', 16 => 'texy-html', 17 => 'texy-html', 18 => 'texy-hr', 19 => 'texy-hr', 20 => 'texy-code', 21 => 'texy-code', 22 => 'texy-hr' + ); + $this->keywords = array( + + ); + + } + + /** + * Finds a delimiter for state LINE_BODY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter0($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '/---', 1 => '\\---', 2 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter1($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (preg_match('~^\\S+~', $part, $matches)) { + return array(1, $matches[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state LINE_SINGLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter2($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '##', 1 => '**', 2 => '==', 3 => '--' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + return array(4, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state LINE_DOUBLE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter3($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n", 1 => '##', 2 => '==', 3 => '--', 4 => '- -', 5 => '**', 6 => '* *' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + if (0 === strpos($part, $delimiters[4])) { + return array(4, $delimiters[4], $buffer); + } + if (0 === strpos($part, $delimiters[5])) { + return array(5, $delimiters[5], $buffer); + } + if (0 === strpos($part, $delimiters[6])) { + return array(6, $delimiters[6], $buffer); + } + return array(7, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEADER_IN. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter4($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '=', 1 => '#', 2 => '-', 3 => '*', 4 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + return array(5, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEADER_BODY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter5($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '=', 1 => '#', 2 => '-', 3 => '*', 4 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + if ($delimiters[1] === $letter) { + return array(1, $delimiters[1], $buffer); + } + if ($delimiters[2] === $letter) { + return array(2, $delimiters[2], $buffer); + } + if ($delimiters[3] === $letter) { + return array(3, $delimiters[3], $buffer); + } + if ($delimiters[4] === $letter) { + return array(4, $delimiters[4], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HEADER_OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter6($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state HORIZONTAL_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter7($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_IN. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter8($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => 'html', 1 => 'code', 2 => 'div', 3 => 'text' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + if (0 === strpos($part, $delimiters[1])) { + return array(1, $delimiters[1], $buffer); + } + if (0 === strpos($part, $delimiters[2])) { + return array(2, $delimiters[2], $buffer); + } + if (0 === strpos($part, $delimiters[3])) { + return array(3, $delimiters[3], $buffer); + } + return array(4, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter9($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_DUMMY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter10($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_TEXT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter11($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_TEXT_BODY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter12($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_TEXT_BODY_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter13($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\\---' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_TEXT_BODY_OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter14($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_HTML. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter15($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_HTML_BODY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter16($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_HTML_BODY_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter17($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\\---' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_HTML_BODY_OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter18($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_CODE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter19($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_CODE_BODY. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter20($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => "\n" + ); + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + if ($delimiters[0] === $letter) { + return array(0, $delimiters[0], $buffer); + } + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_CODE_BODY_LINE. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter21($text, $textLength, $textPos) + { + static $delimiters = array( + 0 => '\\---' + ); + + $buffer = false; + while ($textPos < $textLength) { + $part = substr($text, $textPos, 10); + $letter = $text[$textPos]; + + if (0 === strpos($part, $delimiters[0])) { + return array(0, $delimiters[0], $buffer); + } + return array(1, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + + /** + * Finds a delimiter for state BLOCK_CODE_BODY_OUT. + * + * @param string $text + * @param string $textLength + * @param string $textPos + * @return array + */ + public function findDelimiter22($text, $textLength, $textPos) + { + + $buffer = false; + while ($textPos < $textLength) { + + $letter = $text[$textPos]; + + return array(0, $letter, $buffer); + $buffer .= $letter; + $textPos++; + } + return array(-1, -1, $buffer); + } + +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Cpp.php b/apigen/libs/FSHL/FSHL/Lexer/Cpp.php new file mode 100644 index 00000000000..06076feda5f --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Cpp.php @@ -0,0 +1,308 @@ + array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALPHA' => array('KEYWORD', Generator::BACK), + '//' => array('COMMENT_LINE', Generator::NEXT), + '#' => array('PREPROC', Generator::NEXT), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '/*' => array('COMMENT_BLOCK', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'KEYWORD' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'NUMBER' => array( + array( + 'x' => array('HEXA', Generator::NEXT), + 'f' => array(Generator::STATE_SELF, Generator::NEXT), + 'DOTNUM' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-num', + null + ), + 'HEXA' => array( + array( + 'L' => array(Generator::STATE_SELF, Generator::NEXT), + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'cpp-num', + null + ), + 'PREPROC' => array( + array( + "\\\n" => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + "\\\xd\xa" => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-preproc', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\"' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-quote', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-quote', + null + ), + 'COMMENT_BLOCK' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-comment', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'cpp-comment', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'cpp-keywords', + array( + 'bool' => 1, + 'break' => 1, + 'case' => 1, + 'catch' => 1, + 'char' => 1, + 'class' => 1, + 'const' => 1, + 'const_cast' => 1, + 'continue' => 1, + 'default' => 1, + 'delete' => 1, + 'deprecated' => 1, + 'dllexport' => 1, + 'dllimport' => 1, + 'do' => 1, + 'double' => 1, + 'dynamic_cast' => 1, + 'else' => 1, + 'enum' => 1, + 'explicit' => 1, + 'extern' => 1, + 'false' => 1, + 'float' => 1, + 'for' => 1, + 'friend' => 1, + 'goto' => 1, + 'if' => 1, + 'inline' => 1, + 'int' => 1, + 'long' => 1, + 'mutable' => 1, + 'naked' => 1, + 'namespace' => 1, + 'new' => 1, + 'noinline' => 1, + 'noreturn' => 1, + 'nothrow' => 1, + 'novtable' => 1, + 'operator' => 1, + 'private' => 1, + 'property' => 1, + 'protected' => 1, + 'public' => 1, + 'register' => 1, + 'reinterpret_cast' => 1, + 'return' => 1, + 'selectany' => 1, + 'short' => 1, + 'signed' => 1, + 'sizeof' => 1, + 'static' => 1, + 'static_cast' => 1, + 'struct' => 1, + 'switch' => 1, + 'template' => 1, + 'this' => 1, + 'thread' => 1, + 'throw' => 1, + 'true' => 1, + 'try' => 1, + 'typedef' => 1, + 'typeid' => 1, + 'typename' => 1, + 'union' => 1, + 'unsigned' => 1, + 'using' => 1, + 'uuid' => 1, + 'virtual' => 1, + 'void' => 1, + 'volatile' => 1, + '__wchar_t' => 1, + 'wchar_t' => 1, + 'while' => 1, + '__abstract' => 1, + '__alignof' => 1, + '__asm' => 1, + '__assume' => 1, + '__based' => 1, + '__box' => 1, + '__cdecl' => 1, + '__declspec' => 1, + '__delegate' => 1, + '__event' => 1, + '__except' => 1, + '__fastcall' => 1, + '__finally' => 1, + '__forceinline' => 1, + '__gc' => 1, + '__hook' => 1, + '__identifier' => 1, + '__if_exists' => 1, + '__if_not_exists' => 1, + '__inline' => 1, + '__int8' => 1, + '__int16' => 1, + '__int32' => 1, + '__int64' => 1, + '__interface' => 1, + '__leave' => 1, + '__m64' => 1, + '__m128' => 1, + '__m128d' => 1, + '__m128i' => 1, + '__multiple_inheritance' => 1, + '__nogc' => 1, + '__noop' => 1, + '__pin' => 1, + '__property' => 1, + '__raise' => 1, + '__sealed' => 1, + '__single_inheritance' => 1, + '__stdcall' => 1, + '__super' => 1, + '__try_cast' => 1, + '__try' => 1, + '__except' => 1, + '__finally' => 1, + '__unhook' => 1, + '__uuidof' => 1, + '__value' => 1, + '__virtual_inheritance' => 1, + '__w64' => 1 + ), + Generator::CASE_SENSITIVE + ); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Css.php b/apigen/libs/FSHL/FSHL/Lexer/Css.php new file mode 100644 index 00000000000..98e8e0c31f2 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Css.php @@ -0,0 +1,255 @@ + array( + array( + 'FUNC' => array('FUNC', Generator::NEXT), + 'ALNUM' => array('TAG', Generator::NEXT), + '*' => array('TAG', Generator::NEXT), + '#' => array('ID', Generator::NEXT), + '.' => array('CLASS', Generator::NEXT), + '{' => array('DEF', Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT), + '@media' => array('MEDIA', Generator::NEXT), + '@' => array('AT_RULE', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + ' array(Generator::STATE_QUIT, Generator::NEXT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'MEDIA' => array( + array( + 'PROPERTY' => array('PROPERTY', Generator::NEXT), + ':' => array('VALUE', Generator::CURRENT), + ';' => array(Generator::STATE_SELF, Generator::CURRENT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + ')' => array(Generator::STATE_RETURN, Generator::CURRENT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-at-rule', + null + ), + 'AT_RULE' => array( + array( + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-at-rule', + null + ), + 'TAG' => array( + array( + '{' => array(Generator::STATE_RETURN, Generator::NEXT), + ',' => array(Generator::STATE_RETURN, Generator::BACK), + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK), + ':' => array('PSEUDO', Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-tag', + null + ), + 'ID' => array( + array( + '{' => array(Generator::STATE_RETURN, Generator::BACK), + ',' => array(Generator::STATE_RETURN, Generator::BACK), + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK), + ':' => array('PSEUDO', Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-id', + null + ), + 'CLASS' => array( + array( + '{' => array(Generator::STATE_RETURN, Generator::BACK), + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK), + ',' => array(Generator::STATE_RETURN, Generator::BACK), + ':' => array('PSEUDO', Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-class', + null + ), + 'PSEUDO' => array( + array( + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK), + ',' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'css-pseudo', + null + ), + 'DEF' => array( + array( + 'PROPERTY' => array('PROPERTY', Generator::NEXT), + ':' => array('VALUE', Generator::CURRENT), + ';' => array(Generator::STATE_SELF, Generator::CURRENT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '}' => array(Generator::STATE_RETURN, Generator::CURRENT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'PROPERTY' => array( + array( + ':' => array(Generator::STATE_RETURN, Generator::BACK), + '}' => array(Generator::STATE_RETURN, Generator::BACK), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-property', + null + ), + 'VALUE' => array( + array( + '#' => array('COLOR', Generator::NEXT), + ';' => array(Generator::STATE_RETURN, Generator::BACK), + 'FUNC' => array('FUNC', Generator::NEXT), + ')' => array(Generator::STATE_RETURN, Generator::BACK), + '}' => array(Generator::STATE_RETURN, Generator::BACK), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '/*' => array('COMMENT', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-value', + null + ), + 'FUNC' => array( + array( + ')' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'ALL' => array('VALUE', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-func', + null + ), + 'COLOR' => array( + array( + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'css-color', + null + ), + 'COMMENT' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'css-comment', + null + ), + 'PHP' => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'xlang', + 'Php' + ), + Generator::STATE_QUIT => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'html-tag', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array( + 'FUNC' => 'preg_match(\'~[a-z]+\\s*\\(~iA\', $text, $matches, 0, $textPos)', + 'PROPERTY' => 'preg_match(\'~[-a-z]+~iA\', $text, $matches, 0, $textPos)', + 'PHP' => 'preg_match(\'~<\\\\?(php|=|(?!xml))~A\', $text, $matches, 0, $textPos)' + ); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Html.php b/apigen/libs/FSHL/FSHL/Lexer/Html.php new file mode 100644 index 00000000000..49ed4592c7b --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Html.php @@ -0,0 +1,218 @@ + array( + array( + '' => array('OUT', Generator::CURRENT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'html-comment', + null + ), + 'PHP' => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'xlang', + 'Php' + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array( + 'PHP' => 'preg_match(\'~<\\\\?(php|=|(?!xml))~A\', $text, $matches, 0, $textPos)' + ); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/HtmlOnly.php b/apigen/libs/FSHL/FSHL/Lexer/HtmlOnly.php new file mode 100644 index 00000000000..66935ab1b6c --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/HtmlOnly.php @@ -0,0 +1,220 @@ + array( + array( + '' => array('OUT', Generator::CURRENT) + ), + Generator::STATE_FLAG_NONE, + 'html-comment', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Java.php b/apigen/libs/FSHL/FSHL/Lexer/Java.php new file mode 100644 index 00000000000..6a2950b0cbc --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Java.php @@ -0,0 +1,221 @@ + array( + array( + 'ALPHA' => array('KEYWORD', Generator::BACK), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '/*' => array('COMMENT_BLOCK', Generator::NEXT), + '//' => array('COMMENT_LINE', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'KEYWORD' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'NUMBER' => array( + array( + 'x' => array('HEXA', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'java-num', + null + ), + 'HEXA' => array( + array( + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'java-num', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\"' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'java-quote', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'java-quote', + null + ), + 'COMMENT_BLOCK' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'java-comment', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'java-comment', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'java-keywords', + array( + 'abstract' => 1, + 'double' => 1, + 'int' => 1, + 'strictfp' => 1, + 'boolean' => 1, + 'else' => 1, + 'interface' => 1, + 'super' => 1, + 'break' => 1, + 'extends' => 1, + 'long' => 1, + 'switch' => 1, + 'byte' => 1, + 'final' => 1, + 'native' => 1, + 'synchronized' => 1, + 'case' => 1, + 'finally' => 1, + 'new' => 1, + 'this' => 1, + 'catch' => 1, + 'float' => 1, + 'package' => 1, + 'throw' => 1, + 'char' => 1, + 'for' => 1, + 'private' => 1, + 'throws' => 1, + 'class' => 1, + 'goto' => 1, + 'protected' => 1, + 'transient' => 1, + 'const' => 1, + 'if' => 1, + 'public' => 1, + 'try' => 1, + 'continue' => 1, + 'implements' => 1, + 'return' => 1, + 'void' => 1, + 'default' => 1, + 'import' => 1, + 'short' => 1, + 'volatile' => 1, + 'do' => 1, + 'instanceof' => 1, + 'static' => 1, + 'while' => 1 + ), + Generator::CASE_SENSITIVE + ); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Javascript.php b/apigen/libs/FSHL/FSHL/Lexer/Javascript.php new file mode 100644 index 00000000000..be48af9cfe4 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Javascript.php @@ -0,0 +1,260 @@ + array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALPHA' => array('KEYWORD', Generator::BACK), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + '.' => array('KEYWORD', Generator::CURRENT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '/*' => array('COMMENT_BLOCK', Generator::NEXT), + '//' => array('COMMENT_LINE', Generator::NEXT), + 'REGEXP' => array('REGEXP', Generator::NEXT), + 'PHP' => array('PHP', Generator::NEXT), + ' array(Generator::STATE_QUIT, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'js-out', + null + ), + 'KEYWORD' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + 'js-out', + null + ), + 'NUMBER' => array( + array( + 'x' => array('HEXA', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK), + ), + Generator::STATE_FLAG_RECURSION, + 'js-num', + null + ), + 'HEXA' => array( + array( + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'js-num', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'js-quote', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'js-quote', + null + ), + 'COMMENT_BLOCK' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'js-comment', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + 'PHP' => array('PHP', Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'js-comment', + null + ), + 'REGEXP' => array( + array( + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'js-quote', + null + ), + 'PHP' => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'xlang', + 'Php' + ), + Generator::STATE_QUIT => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'html-tag', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array( + 'REGEXP' => 'preg_match(\'~/.*?[^\\\\\\\\]/[gim]*~A\', $text, $matches, 0, $textPos)', + 'PHP' => 'preg_match(\'~<\\\\?(php|=|(?!xml))~A\', $text, $matches, 0, $textPos)' + ); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'js-keywords', + array( + 'abstract' => 1, + 'boolean' => 1, + 'break' => 1, + 'byte' => 1, + 'case' => 1, + 'catch' => 1, + 'char' => 1, + 'class' => 1, + 'const' => 1, + 'continue' => 1, + 'debugger' => 1, + 'default' => 1, + 'delete' => 1, + 'do' => 1, + 'double' => 1, + 'else' => 1, + 'enum' => 1, + 'export' => 1, + 'extends' => 1, + 'false' => 1, + 'final' => 1, + 'finally' => 1, + 'float' => 1, + 'for' => 1, + 'function' => 1, + 'goto' => 1, + 'if' => 1, + 'implements' => 1, + 'import' => 1, + 'in' => 1, + 'instanceof' => 1, + 'int' => 1, + 'interface' => 1, + 'long' => 1, + 'native' => 1, + 'new' => 1, + 'null' => 1, + 'package' => 1, + 'private' => 1, + 'protected' => 1, + 'public' => 1, + 'return' => 1, + 'short' => 1, + 'static' => 1, + 'super' => 1, + 'switch' => 1, + 'synchronized' => 1, + 'this' => 1, + 'throw' => 1, + 'throws' => 1, + 'transient' => 1, + 'true' => 1, + 'try' => 1, + 'typeof' => 1, + 'var' => 1, + 'void' => 1, + 'volatile' => 1, + 'while' => 1, + 'with' => 1, + + 'document' => 2, + 'getAttribute' => 2, + 'getElementsByTagName' => 2, + 'getElementById' => 2, + ), + Generator::CASE_SENSITIVE + ); + } +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Lexer/Minimal.php b/apigen/libs/FSHL/FSHL/Lexer/Minimal.php new file mode 100644 index 00000000000..028db9e0412 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Minimal.php @@ -0,0 +1,92 @@ + array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Neon.php b/apigen/libs/FSHL/FSHL/Lexer/Neon.php new file mode 100644 index 00000000000..e6bb7804bf4 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Neon.php @@ -0,0 +1,230 @@ + array( + array( + 'SECTION' => array('SECTION', Generator::NEXT), + 'KEY' => array('KEY', Generator::NEXT), + '#' => array('COMMENT', Generator::NEXT), + '-' => array('LIST', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'SECTION' => array( + array( + '<' => array('SEPARATOR', Generator::NEXT), + ':' => array('SEPARATOR', Generator::NEXT), + 'SECTION' => array(Generator::STATE_SELF, Generator::NEXT), + 'KEY' => array('KEY', Generator::NEXT), + '-' => array('LIST', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'neon-section', + null + ), + 'KEY' => array( + array( + ':' => array('SEPARATOR', Generator::NEXT), + '=' => array('SEPARATOR', Generator::NEXT), + 'ALL' => array('VALUE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'neon-key', + null + ), + 'LIST' => array( + array( + 'ALL' => array('VALUE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'neon-sep', + null + ), + 'VALUE' => array( + array( + 'LINE' => array('OUT', Generator::NEXT), + 'KEY' => array('KEY', Generator::NEXT), + '#' => array('COMMENT', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '[' => array('SEPARATOR', Generator::NEXT), + ']' => array('SEPARATOR', Generator::NEXT), + '{' => array('SEPARATOR', Generator::NEXT), + '}' => array('SEPARATOR', Generator::NEXT), + '=' => array('SEPARATOR', Generator::NEXT), + ',' => array('SEPARATOR', Generator::NEXT), + ':' => array('SEPARATOR', Generator::NEXT), + 'TEXT' => array('TEXT', Generator::NEXT), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + 'VARIABLE' => array('VARIABLE', Generator::NEXT), + '@' => array('REFERENCE', Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'neon-value', + null + ), + 'TEXT' => array( + array( + 'VARIABLE' => array('VARIABLE', Generator::NEXT), + '#' => array('COMMENT', Generator::NEXT), + 'LINE' => array('OUT', Generator::NEXT), + ), + Generator::STATE_FLAG_NONE, + 'neon-value', + null + ), + 'SEPARATOR' => array( + array( + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-sep', + null + ), + 'COMMENT' => array( + array( + 'LINE' => array('OUT', Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'neon-comment', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'VARIABLE' => array('VARIABLE', Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-quote', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'VARIABLE' => array('VARIABLE', Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-quote', + null + ), + 'VARIABLE' => array( + array( + '%' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-var', + null + ), + 'NUMBER' => array( + array( + 'DOTNUM' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-num', + null + ), + 'REFERENCE' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'neon-ref', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array( + 'SECTION' => 'preg_match(\'~[\\\\w.]+(?=(\\\\s*<\\\\s*[\\\\w.]+)?\\\\s*:\\\\s*\\n)~Ai\', $text, $matches, 0, $textPos)', + 'KEY' => 'preg_match(\'~[\\\\w.]+(?=\\\\s*(?::|=))~Ai\', $text, $matches, 0, $textPos)', + 'VARIABLE' => 'preg_match(\'~%\\\\w+(?=%)~Ai\', $text, $matches, 0, $textPos)', + 'TEXT' => 'preg_match(\'~[a-z](?![,\\\\]}#\\n])~Ai\', $text, $matches, 0, $textPos)' + ); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Php.php b/apigen/libs/FSHL/FSHL/Lexer/Php.php new file mode 100644 index 00000000000..9679dfe0a30 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Php.php @@ -0,0 +1,3655 @@ + array( + array( + '$' => array('VAR', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + 'ALPHA' => array('FUNCTION', Generator::BACK), + '_' => array('FUNCTION', Generator::BACK), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '//' => array('COMMENT_LINE', Generator::NEXT), + '/*' => array('COMMENT_BLOCK', Generator::NEXT), + '#' => array('COMMENT_LINE', Generator::NEXT), + '?>' => array(Generator::STATE_QUIT, Generator::NEXT), + ' array('DUMMY_PHP', Generator::BACK), + 'NOWDOC' => array('NOWDOC', Generator::NEXT), + 'HEREDOC' => array('HEREDOC', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'DUMMY_PHP' => array( + array( + ' array(Generator::STATE_RETURN, Generator::CURRENT), + ' array(Generator::STATE_RETURN, Generator::CURRENT), + ' array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'xlang', + null + ), + 'FUNCTION' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'COMMENT_BLOCK' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'php-comment', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '?>' => array(Generator::STATE_RETURN, Generator::CURRENT) + ), + Generator::STATE_FLAG_RECURSION, + 'php-comment', + null + ), + 'VAR' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK), + '$' => array(Generator::STATE_SELF, Generator::NEXT), + '{' => array(Generator::STATE_SELF, Generator::NEXT), + '}' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'php-var', + null + ), + 'VAR_STR' => array( + array( + '}' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'SPACE' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'php-var', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\"' => array(Generator::STATE_SELF, Generator::NEXT), + '$' => array('VAR', Generator::NEXT), + '{$' => array('VAR_STR', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'php-quote', + null + ), + 'HEREDOC' => array( + array( + 'LINE' => array('HEREDOC_END', Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '\\$' => array(Generator::STATE_SELF, Generator::NEXT), + '$' => array('VAR', Generator::NEXT), + '{$' => array('VAR_STR', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'php-quote', + null + ), + 'HEREDOC_END' => array( + array( + 'HEREDOC_NOWDOC_END' => array('OUT', Generator::CURRENT), + 'ALL' => array('HEREDOC', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'php-quote', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'php-quote', + null + ), + 'NOWDOC' => array( + array( + 'LINE' => array('NOWDOC_END', Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'php-quote', + null + ), + 'NOWDOC_END' => array( + array( + 'HEREDOC_NOWDOC_END' => array('OUT', Generator::CURRENT), + 'ALL' => array('NOWDOC', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'php-quote', + null + ), + 'NUMBER' => array( + array( + 'e' => array('EXPONENT', Generator::NEXT), + 'E' => array('EXPONENT', Generator::NEXT), + 'x' => array('HEXA', Generator::NEXT), + 'b' => array(Generator::STATE_SELF, Generator::NEXT), + 'DOTNUM' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'php-num', + null + ), + 'EXPONENT' => array( + array( + '+' => array(Generator::STATE_SELF, Generator::CURRENT), + '-' => array(Generator::STATE_SELF, Generator::CURRENT), + '!NUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'php-num', + null + ), + 'HEXA' => array( + array( + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'php-num', + null + ), + Generator::STATE_QUIT => array( + null, + Generator::STATE_FLAG_NEWLEXER, + 'xlang', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array( + 'HEREDOC' => 'preg_match(\'~<<<(?:\\\\w+|"\\\\w+")\\\\n~A\', $text, $matches, 0, $textPos)', + 'NOWDOC' => 'preg_match(\'~<<<\\\'\\\\w+\\\'\\\\n~A\', $text, $matches, 0, $textPos)', + // Starting \n is missing intentionally + 'HEREDOC_NOWDOC_END' => 'preg_match(\'~\\\\w+;\\\\n~A\', $text, $matches, 0, $textPos)' + ); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'php-keyword', + array( + // Keywords + 'abstract' => 1, + 'and' => 1, + 'array' => 1, + 'as' => 1, + 'break' => 1, + 'callable' => 1, + 'case' => 1, + 'catch' => 1, + 'class' => 1, + 'clone' => 1, + 'const' => 1, + 'continue' => 1, + 'declare' => 1, + 'default' => 1, + 'do' => 1, + 'else' => 1, + 'elseif' => 1, + 'enddeclare' => 1, + 'endfor' => 1, + 'endforeach' => 1, + 'endif' => 1, + 'endswitch' => 1, + 'endwhile' => 1, + 'extends' => 1, + 'final' => 1, + 'for' => 1, + 'foreach' => 1, + 'function' => 1, + 'global' => 1, + 'goto' => 1, + 'if' => 1, + 'implements' => 1, + 'interface' => 1, + 'instanceof' => 1, + 'insteadof' => 1, + 'namespace' => 1, + 'new' => 1, + 'or' => 1, + 'private' => 1, + 'protected' => 1, + 'public' => 1, + 'static' => 1, + 'switch' => 1, + 'throw' => 1, + 'trait' => 1, + 'try' => 1, + 'use' => 1, + 'var' => 1, + 'while' => 1, + 'xor' => 1, + + // Compile-time constants + '__CLASS__' => 1, + '__DIR__' => 1, + '__FILE__' => 1, + '__LINE__' => 1, + '__FUNCTION__' => 1, + '__METHOD__' => 1, + '__NAMESPACE__' => 1, + '__TRAIT__' => 1, + + // Language constructs + 'die' => 1, + 'echo' => 1, + 'empty' => 1, + 'exit' => 1, + 'eval' => 1, + 'include' => 1, + 'include_once' => 1, + 'isset' => 1, + 'list' => 1, + 'require' => 1, + 'require_once' => 1, + 'return' => 1, + 'print' => 1, + 'unset' => 1, + + // Types + 'true' => 1, + 'false' => 1, + 'null' => 1, + + // Functions list + 'abs' => 2, + 'acos' => 2, + 'acosh' => 2, + 'ada_afetch' => 2, + 'ada_autocommit' => 2, + 'ada_close' => 2, + 'ada_commit' => 2, + 'ada_connect' => 2, + 'ada_exec' => 2, + 'ada_fetchrow' => 2, + 'ada_fieldname' => 2, + 'ada_fieldnum' => 2, + 'ada_fieldtype' => 2, + 'ada_freeresult' => 2, + 'ada_numfields' => 2, + 'ada_numrows' => 2, + 'ada_result' => 2, + 'ada_resultall' => 2, + 'ada_rollback' => 2, + 'addcslashes' => 2, + 'addslashes' => 2, + 'aggregate' => 2, + 'aggregate_methods' => 2, + 'aggregate_methods_by_list' => 2, + 'aggregate_methods_by_regexp' => 2, + 'aggregate_properties' => 2, + 'aggregate_properties_by_list' => 2, + 'aggregate_properties_by_regexp' => 2, + 'aggregation_info' => 2, + 'apache_child_terminate' => 2, + 'apache_get_modules' => 2, + 'apache_get_version' => 2, + 'apache_getenv' => 2, + 'apache_lookup_uri' => 2, + 'apache_note' => 2, + 'apache_request_headers' => 2, + 'apache_response_headers' => 2, + 'apache_setenv' => 2, + 'apc_add' => 2, + 'apc_bin_dump' => 2, + 'apc_bin_dumpfile' => 2, + 'apc_bin_load' => 2, + 'apc_bin_loadfile' => 2, + 'apc_cache_info' => 2, + 'apc_cas' => 2, + 'apc_clear_cache' => 2, + 'apc_compile_file' => 2, + 'apc_dec' => 2, + 'apc_define_constants' => 2, + 'apc_delete' => 2, + 'apc_delete_file' => 2, + 'apc_exists' => 2, + 'apc_fetch' => 2, + 'apc_inc' => 2, + 'apc_load_constants' => 2, + 'apc_sma_info' => 2, + 'apc_store' => 2, + 'array_change_key_case' => 2, + 'array_chunk' => 2, + 'array_combine' => 2, + 'array_count_values' => 2, + 'array_diff' => 2, + 'array_diff_assoc' => 2, + 'array_diff_key' => 2, + 'array_diff_uassoc' => 2, + 'array_diff_ukey' => 2, + 'array_fill' => 2, + 'array_fill_keys' => 2, + 'array_filter' => 2, + 'array_flip' => 2, + 'array_intersect' => 2, + 'array_intersect_assoc' => 2, + 'array_intersect_key' => 2, + 'array_intersect_uassoc' => 2, + 'array_intersect_ukey' => 2, + 'array_key_exists' => 2, + 'array_keys' => 2, + 'array_map' => 2, + 'array_merge' => 2, + 'array_merge_recursive' => 2, + 'array_multisort' => 2, + 'array_pad' => 2, + 'array_pop' => 2, + 'array_product' => 2, + 'array_push' => 2, + 'array_rand' => 2, + 'array_reduce' => 2, + 'array_replace' => 2, + 'array_replace_recursive' => 2, + 'array_reverse' => 2, + 'array_search' => 2, + 'array_shift' => 2, + 'array_slice' => 2, + 'array_splice' => 2, + 'array_sum' => 2, + 'array_udiff' => 2, + 'array_udiff_assoc' => 2, + 'array_udiff_uassoc' => 2, + 'array_uintersect' => 2, + 'array_uintersect_assoc' => 2, + 'array_uintersect_uassoc' => 2, + 'array_unique' => 2, + 'array_unshift' => 2, + 'array_values' => 2, + 'array_walk' => 2, + 'array_walk_recursive' => 2, + 'arsort' => 2, + 'asin' => 2, + 'asinh' => 2, + 'asort' => 2, + 'aspell_check' => 2, + 'aspell_check-raw' => 2, + 'aspell_new' => 2, + 'aspell_suggest' => 2, + 'assert' => 2, + 'assert_options' => 2, + 'atan' => 2, + 'atan2' => 2, + 'atanh' => 2, + 'base64_decode' => 2, + 'base64_encode' => 2, + 'base_convert' => 2, + 'basename' => 2, + 'bcadd' => 2, + 'bccomp' => 2, + 'bcdiv' => 2, + 'bcmod' => 2, + 'bcmul' => 2, + 'bcompiler_load' => 2, + 'bcompiler_load_exe' => 2, + 'bcompiler_parse_class' => 2, + 'bcompiler_read' => 2, + 'bcompiler_write_class' => 2, + 'bcompiler_write_constant' => 2, + 'bcompiler_write_exe_footer' => 2, + 'bcompiler_write_file' => 2, + 'bcompiler_write_footer' => 2, + 'bcompiler_write_function' => 2, + 'bcompiler_write_functions_from_file' => 2, + 'bcompiler_write_header' => 2, + 'bcompiler_write_included_filename' => 2, + 'bcpow' => 2, + 'bcpowmod' => 2, + 'bcscale' => 2, + 'bcsqrt' => 2, + 'bcsub' => 2, + 'bin2hex' => 2, + 'bind_textdomain_codeset' => 2, + 'bindec' => 2, + 'bindtextdomain' => 2, + 'bitset_empty' => 2, + 'bitset_equal' => 2, + 'bitset_excl' => 2, + 'bitset_fill' => 2, + 'bitset_from_array' => 2, + 'bitset_from_hash' => 2, + 'bitset_from_string' => 2, + 'bitset_in' => 2, + 'bitset_incl' => 2, + 'bitset_intersection' => 2, + 'bitset_invert' => 2, + 'bitset_is_empty' => 2, + 'bitset_subset' => 2, + 'bitset_to_array' => 2, + 'bitset_to_hash' => 2, + 'bitset_to_string' => 2, + 'bitset_union' => 2, + 'blenc_encrypt' => 2, + 'bzclose' => 2, + 'bzcompress' => 2, + 'bzdecompress' => 2, + 'bzerrno' => 2, + 'bzerror' => 2, + 'bzerrstr' => 2, + 'bzflush' => 2, + 'bzopen' => 2, + 'bzread' => 2, + 'bzwrite' => 2, + 'cal_days_in_month' => 2, + 'cal_from_jd' => 2, + 'cal_info' => 2, + 'cal_to_jd' => 2, + 'call_user_func' => 2, + 'call_user_func_array' => 2, + 'call_user_method' => 2, + 'call_user_method_array' => 2, + 'ceil' => 2, + 'chdir' => 2, + 'checkdate' => 2, + 'checkdnsrr' => 2, + 'chgrp' => 2, + 'chmod' => 2, + 'chop' => 2, + 'chown' => 2, + 'chr' => 2, + 'chunk_split' => 2, + 'class_alias' => 2, + 'class_exists' => 2, + 'class_implements' => 2, + 'class_parents' => 2, + 'classkit_aggregate_methods' => 2, + 'classkit_doc_comments' => 2, + 'classkit_import' => 2, + 'classkit_method_add' => 2, + 'classkit_method_copy' => 2, + 'classkit_method_redefine' => 2, + 'classkit_method_remove' => 2, + 'classkit_method_rename' => 2, + 'clearstatcache' => 2, + 'closedir' => 2, + 'closelog' => 2, + 'collator_asort' => 2, + 'collator_compare' => 2, + 'collator_create' => 2, + 'collator_get_attribute' => 2, + 'collator_get_error_code' => 2, + 'collator_get_error_message' => 2, + 'collator_get_locale' => 2, + 'collator_get_sort_key' => 2, + 'collator_get_strength' => 2, + 'collator_set_attribute' => 2, + 'collator_set_strength' => 2, + 'collator_sort' => 2, + 'collator_sort_with_sort_keys' => 2, + 'com_create_guid' => 2, + 'com_event_sink' => 2, + 'com_get_active_object' => 2, + 'com_load_typelib' => 2, + 'com_message_pump' => 2, + 'com_print_typeinfo' => 2, + 'compact' => 2, + 'confirm_phpdoc_compiled' => 2, + 'connection_aborted' => 2, + 'connection_status' => 2, + 'connection_timeout' => 2, + 'constant' => 2, + 'contained' => 2, + 'convert_cyr_string' => 2, + 'convert_uudecode' => 2, + 'convert_uuencode' => 2, + 'copy' => 2, + 'cos' => 2, + 'cosh' => 2, + 'count' => 2, + 'count_chars' => 2, + 'cpdf_add_annotation' => 2, + 'cpdf_add_outline' => 2, + 'cpdf_arc' => 2, + 'cpdf_begin_text' => 2, + 'cpdf_circle' => 2, + 'cpdf_clip' => 2, + 'cpdf_close' => 2, + 'cpdf_closepath' => 2, + 'cpdf_closepath_fill_stroke' => 2, + 'cpdf_closepath_stroke' => 2, + 'cpdf_continue_text' => 2, + 'cpdf_curveto' => 2, + 'cpdf_end_text' => 2, + 'cpdf_fill' => 2, + 'cpdf_fill_stroke' => 2, + 'cpdf_finalize' => 2, + 'cpdf_finalize_page' => 2, + 'cpdf_global_set_document_limits' => 2, + 'cpdf_import_jpeg' => 2, + 'cpdf_lineto' => 2, + 'cpdf_moveto' => 2, + 'cpdf_newpath' => 2, + 'cpdf_open' => 2, + 'cpdf_output_buffer' => 2, + 'cpdf_page_init' => 2, + 'cpdf_place_inline_image' => 2, + 'cpdf_rect' => 2, + 'cpdf_restore' => 2, + 'cpdf_rlineto' => 2, + 'cpdf_rmoveto' => 2, + 'cpdf_rotate' => 2, + 'cpdf_rotate_text' => 2, + 'cpdf_save' => 2, + 'cpdf_save_to_file' => 2, + 'cpdf_scale' => 2, + 'cpdf_set_action_url' => 2, + 'cpdf_set_char_spacing' => 2, + 'cpdf_set_creator' => 2, + 'cpdf_set_current_page' => 2, + 'cpdf_set_font' => 2, + 'cpdf_set_font_directories' => 2, + 'cpdf_set_font_map_file' => 2, + 'cpdf_set_horiz_scaling' => 2, + 'cpdf_set_keywords' => 2, + 'cpdf_set_leading' => 2, + 'cpdf_set_page_animation' => 2, + 'cpdf_set_subject' => 2, + 'cpdf_set_text_matrix' => 2, + 'cpdf_set_text_pos' => 2, + 'cpdf_set_text_rendering' => 2, + 'cpdf_set_text_rise' => 2, + 'cpdf_set_title' => 2, + 'cpdf_set_viewer_preferences' => 2, + 'cpdf_set_word_spacing' => 2, + 'cpdf_setdash' => 2, + 'cpdf_setflat' => 2, + 'cpdf_setgray' => 2, + 'cpdf_setgray_fill' => 2, + 'cpdf_setgray_stroke' => 2, + 'cpdf_setlinecap' => 2, + 'cpdf_setlinejoin' => 2, + 'cpdf_setlinewidth' => 2, + 'cpdf_setmiterlimit' => 2, + 'cpdf_setrgbcolor' => 2, + 'cpdf_setrgbcolor_fill' => 2, + 'cpdf_setrgbcolor_stroke' => 2, + 'cpdf_show' => 2, + 'cpdf_show_xy' => 2, + 'cpdf_stringwidth' => 2, + 'cpdf_stroke' => 2, + 'cpdf_text' => 2, + 'cpdf_translate' => 2, + 'crack_check' => 2, + 'crack_closedict' => 2, + 'crack_getlastmessage' => 2, + 'crack_opendict' => 2, + 'crc32' => 2, + 'create_function' => 2, + 'crypt' => 2, + 'ctype_alnum' => 2, + 'ctype_alpha' => 2, + 'ctype_cntrl' => 2, + 'ctype_digit' => 2, + 'ctype_graph' => 2, + 'ctype_lower' => 2, + 'ctype_print' => 2, + 'ctype_punct' => 2, + 'ctype_space' => 2, + 'ctype_upper' => 2, + 'ctype_xdigit' => 2, + 'curl_close' => 2, + 'curl_copy_handle' => 2, + 'curl_errno' => 2, + 'curl_error' => 2, + 'curl_exec' => 2, + 'curl_getinfo' => 2, + 'curl_init' => 2, + 'curl_multi_add_handle' => 2, + 'curl_multi_close' => 2, + 'curl_multi_exec' => 2, + 'curl_multi_getcontent' => 2, + 'curl_multi_info_read' => 2, + 'curl_multi_init' => 2, + 'curl_multi_remove_handle' => 2, + 'curl_multi_select' => 2, + 'curl_setopt' => 2, + 'curl_setopt_array' => 2, + 'curl_version' => 2, + 'current' => 2, + 'cvsclient_connect' => 2, + 'cvsclient_log' => 2, + 'cvsclient_login' => 2, + 'cvsclient_retrieve' => 2, + 'date' => 2, + 'date_add' => 2, + 'date_create' => 2, + 'date_create_from_format' => 2, + 'date_date_set' => 2, + 'date_default_timezone_get' => 2, + 'date_default_timezone_set' => 2, + 'date_diff' => 2, + 'date_format' => 2, + 'date_get_last_errors' => 2, + 'date_interval_create_from_date_string' => 2, + 'date_interval_format' => 2, + 'date_isodate_set' => 2, + 'date_modify' => 2, + 'date_offset_get' => 2, + 'date_parse' => 2, + 'date_parse_from_format' => 2, + 'date_sub' => 2, + 'date_sun_info' => 2, + 'date_sunrise' => 2, + 'date_sunset' => 2, + 'date_time_set' => 2, + 'date_timestamp_get' => 2, + 'date_timestamp_set' => 2, + 'date_timezone_get' => 2, + 'date_timezone_set' => 2, + 'datefmt_create' => 2, + 'datefmt_format' => 2, + 'datefmt_get_calendar' => 2, + 'datefmt_get_datetype' => 2, + 'datefmt_get_error_code' => 2, + 'datefmt_get_error_message' => 2, + 'datefmt_get_locale' => 2, + 'datefmt_get_pattern' => 2, + 'datefmt_get_timetype' => 2, + 'datefmt_get_timezone_id' => 2, + 'datefmt_is_lenient' => 2, + 'datefmt_localtime' => 2, + 'datefmt_parse' => 2, + 'datefmt_set_calendar' => 2, + 'datefmt_set_lenient' => 2, + 'datefmt_set_pattern' => 2, + 'datefmt_set_timezone_id' => 2, + 'db_id_list' => 2, + 'dba_close' => 2, + 'dba_delete' => 2, + 'dba_exists' => 2, + 'dba_fetch' => 2, + 'dba_firstkey' => 2, + 'dba_handlers' => 2, + 'dba_insert' => 2, + 'dba_key_split' => 2, + 'dba_list' => 2, + 'dba_nextkey' => 2, + 'dba_open' => 2, + 'dba_optimize' => 2, + 'dba_popen' => 2, + 'dba_replace' => 2, + 'dba_sync' => 2, + 'dbase_add_record' => 2, + 'dbase_close' => 2, + 'dbase_create' => 2, + 'dbase_delete_record' => 2, + 'dbase_get_header_info' => 2, + 'dbase_get_record' => 2, + 'dbase_get_record_with_names' => 2, + 'dbase_numfields' => 2, + 'dbase_numrecords' => 2, + 'dbase_open' => 2, + 'dbase_pack' => 2, + 'dbase_replace_record' => 2, + 'dbg_get_all_contexts' => 2, + 'dbg_get_all_module_names' => 2, + 'dbg_get_all_source_lines' => 2, + 'dbg_get_context_name' => 2, + 'dbg_get_loaded_zendextensions' => 2, + 'dbg_get_module_name' => 2, + 'dbg_get_profiler_results' => 2, + 'dbg_get_source_context' => 2, + 'dblist' => 2, + 'dbmclose' => 2, + 'dbmdelete' => 2, + 'dbmexists' => 2, + 'dbmfetch' => 2, + 'dbmfirstkey' => 2, + 'dbminsert' => 2, + 'dbmnextkey' => 2, + 'dbmopen' => 2, + 'dbmreplace' => 2, + 'dbx_close' => 2, + 'dbx_compare' => 2, + 'dbx_connect' => 2, + 'dbx_error' => 2, + 'dbx_escape_string' => 2, + 'dbx_fetch_row' => 2, + 'dbx_query' => 2, + 'dbx_sort' => 2, + 'dcgettext' => 2, + 'dcngettext' => 2, + 'deaggregate' => 2, + 'debug_backtrace' => 2, + 'debug_print_backtrace' => 2, + 'debug_zval_dump' => 2, + 'debugbreak' => 2, + 'debugger_off' => 2, + 'debugger_on' => 2, + 'decbin' => 2, + 'dechex' => 2, + 'decoct' => 2, + 'define' => 2, + 'define_syslog_variables' => 2, + 'defined' => 2, + 'deg2rad' => 2, + 'delete' => 2, + 'dgettext' => 2, + 'dio_close' => 2, + 'dio_open' => 2, + 'dio_read' => 2, + 'dio_seek' => 2, + 'dio_stat' => 2, + 'dio_write' => 2, + 'dir' => 2, + 'dirname' => 2, + 'disk_free_space' => 2, + 'disk_total_space' => 2, + 'diskfreespace' => 2, + 'dl' => 2, + 'dngettext' => 2, + 'dns_check_record' => 2, + 'dns_get_mx' => 2, + 'dns_get_record' => 2, + 'docblock_token_name' => 2, + 'docblock_tokenize' => 2, + 'dom_import_simplexml' => 2, + 'domxml_add_root' => 2, + 'domxml_attributes' => 2, + 'domxml_children' => 2, + 'domxml_doc_add_root' => 2, + 'domxml_doc_document_element' => 2, + 'domxml_doc_get_element_by_id' => 2, + 'domxml_doc_get_elements_by_tagname' => 2, + 'domxml_doc_get_root' => 2, + 'domxml_doc_set_root' => 2, + 'domxml_doc_validate' => 2, + 'domxml_doc_xinclude' => 2, + 'domxml_dump_mem' => 2, + 'domxml_dump_mem_file' => 2, + 'domxml_dump_node' => 2, + 'domxml_dumpmem' => 2, + 'domxml_elem_get_attribute' => 2, + 'domxml_elem_set_attribute' => 2, + 'domxml_get_attribute' => 2, + 'domxml_getattr' => 2, + 'domxml_html_dump_mem' => 2, + 'domxml_new_child' => 2, + 'domxml_new_doc' => 2, + 'domxml_new_xmldoc' => 2, + 'domxml_node' => 2, + 'domxml_node_add_namespace' => 2, + 'domxml_node_attributes' => 2, + 'domxml_node_children' => 2, + 'domxml_node_get_content' => 2, + 'domxml_node_has_attributes' => 2, + 'domxml_node_new_child' => 2, + 'domxml_node_set_content' => 2, + 'domxml_node_set_namespace' => 2, + 'domxml_node_unlink_node' => 2, + 'domxml_open_file' => 2, + 'domxml_open_mem' => 2, + 'domxml_parser' => 2, + 'domxml_parser_add_chunk' => 2, + 'domxml_parser_cdata_section' => 2, + 'domxml_parser_characters' => 2, + 'domxml_parser_comment' => 2, + 'domxml_parser_end' => 2, + 'domxml_parser_end_document' => 2, + 'domxml_parser_end_element' => 2, + 'domxml_parser_entity_reference' => 2, + 'domxml_parser_get_document' => 2, + 'domxml_parser_namespace_decl' => 2, + 'domxml_parser_processing_instruction' => 2, + 'domxml_parser_start_document' => 2, + 'domxml_parser_start_element' => 2, + 'domxml_root' => 2, + 'domxml_set_attribute' => 2, + 'domxml_setattr' => 2, + 'domxml_substitute_entities_default' => 2, + 'domxml_unlink_node' => 2, + 'domxml_version' => 2, + 'domxml_xmltree' => 2, + 'doubleval' => 2, + 'each' => 2, + 'easter_date' => 2, + 'easter_days' => 2, + 'end' => 2, + 'endwhile' => 2, + 'ereg' => 2, + 'ereg_replace' => 2, + 'eregi' => 2, + 'eregi_replace' => 2, + 'error_get_last' => 2, + 'error_log' => 2, + 'error_reporting' => 2, + 'escapeshellarg' => 2, + 'escapeshellcmd' => 2, + 'eval' => 2, + 'event_deschedule' => 2, + 'event_dispatch' => 2, + 'event_free' => 2, + 'event_handle_signal' => 2, + 'event_have_events' => 2, + 'event_init' => 2, + 'event_new' => 2, + 'event_pending' => 2, + 'event_priority_set' => 2, + 'event_schedule' => 2, + 'event_set' => 2, + 'event_timeout' => 2, + 'exec' => 2, + 'exif_imagetype' => 2, + 'exif_read_data' => 2, + 'exif_tagname' => 2, + 'exif_thumbnail' => 2, + 'exp' => 2, + 'explode' => 2, + 'expm1' => 2, + 'extension_loaded' => 2, + 'extract' => 2, + 'ezmlm_hash' => 2, + 'fbird_add_user' => 2, + 'fbird_affected_rows' => 2, + 'fbird_backup' => 2, + 'fbird_blob_add' => 2, + 'fbird_blob_cancel' => 2, + 'fbird_blob_close' => 2, + 'fbird_blob_create' => 2, + 'fbird_blob_echo' => 2, + 'fbird_blob_get' => 2, + 'fbird_blob_import' => 2, + 'fbird_blob_info' => 2, + 'fbird_blob_open' => 2, + 'fbird_close' => 2, + 'fbird_commit' => 2, + 'fbird_commit_ret' => 2, + 'fbird_connect' => 2, + 'fbird_db_info' => 2, + 'fbird_delete_user' => 2, + 'fbird_drop_db' => 2, + 'fbird_errcode' => 2, + 'fbird_errmsg' => 2, + 'fbird_execute' => 2, + 'fbird_fetch_assoc' => 2, + 'fbird_fetch_object' => 2, + 'fbird_fetch_row' => 2, + 'fbird_field_info' => 2, + 'fbird_free_event_handler' => 2, + 'fbird_free_query' => 2, + 'fbird_free_result' => 2, + 'fbird_gen_id' => 2, + 'fbird_maintain_db' => 2, + 'fbird_modify_user' => 2, + 'fbird_name_result' => 2, + 'fbird_num_fields' => 2, + 'fbird_num_params' => 2, + 'fbird_param_info' => 2, + 'fbird_pconnect' => 2, + 'fbird_prepare' => 2, + 'fbird_query' => 2, + 'fbird_restore' => 2, + 'fbird_rollback' => 2, + 'fbird_rollback_ret' => 2, + 'fbird_server_info' => 2, + 'fbird_service_attach' => 2, + 'fbird_service_detach' => 2, + 'fbird_set_event_handler' => 2, + 'fbird_trans' => 2, + 'fbird_wait_event' => 2, + 'fclose' => 2, + 'fdf_add_doc_javascript' => 2, + 'fdf_add_template' => 2, + 'fdf_close' => 2, + 'fdf_create' => 2, + 'fdf_enum_values' => 2, + 'fdf_errno' => 2, + 'fdf_error' => 2, + 'fdf_get_ap' => 2, + 'fdf_get_attachment' => 2, + 'fdf_get_encoding' => 2, + 'fdf_get_file' => 2, + 'fdf_get_flags' => 2, + 'fdf_get_opt' => 2, + 'fdf_get_status' => 2, + 'fdf_get_value' => 2, + 'fdf_get_version' => 2, + 'fdf_header' => 2, + 'fdf_next_field_name' => 2, + 'fdf_open' => 2, + 'fdf_open_string' => 2, + 'fdf_remove_item' => 2, + 'fdf_save' => 2, + 'fdf_save_string' => 2, + 'fdf_set_ap' => 2, + 'fdf_set_encoding' => 2, + 'fdf_set_file' => 2, + 'fdf_set_flags' => 2, + 'fdf_set_javascript_action' => 2, + 'fdf_set_on_import_javascript' => 2, + 'fdf_set_opt' => 2, + 'fdf_set_status' => 2, + 'fdf_set_submit_form_action' => 2, + 'fdf_set_target_frame' => 2, + 'fdf_set_value' => 2, + 'fdf_set_version' => 2, + 'feof' => 2, + 'fflush' => 2, + 'fgetc' => 2, + 'fgetcsv' => 2, + 'fgets' => 2, + 'fgetss' => 2, + 'file' => 2, + 'file_exists' => 2, + 'file_get_contents' => 2, + 'file_put_contents' => 2, + 'fileatime' => 2, + 'filectime' => 2, + 'filegroup' => 2, + 'fileinode' => 2, + 'filemtime' => 2, + 'fileowner' => 2, + 'fileperms' => 2, + 'filepro' => 2, + 'filepro_fieldcount' => 2, + 'filepro_fieldname' => 2, + 'filepro_fieldtype' => 2, + 'filepro_fieldwidth' => 2, + 'filepro_retrieve' => 2, + 'filepro_rowcount' => 2, + 'filesize' => 2, + 'filetype' => 2, + 'filter_has_var' => 2, + 'filter_id' => 2, + 'filter_input' => 2, + 'filter_input_array' => 2, + 'filter_list' => 2, + 'filter_var' => 2, + 'filter_var_array' => 2, + 'finfo_buffer' => 2, + 'finfo_close' => 2, + 'finfo_file' => 2, + 'finfo_open' => 2, + 'finfo_set_flags' => 2, + 'floatval' => 2, + 'flock' => 2, + 'floor' => 2, + 'flush' => 2, + 'fmod' => 2, + 'fnmatch' => 2, + 'fopen' => 2, + 'forward_static_call' => 2, + 'forward_static_call_array' => 2, + 'fpassthru' => 2, + 'fprintf' => 2, + 'fputcsv' => 2, + 'fputs' => 2, + 'fread' => 2, + 'frenchtojd' => 2, + 'fribidi_charset_info' => 2, + 'fribidi_get_charsets' => 2, + 'fribidi_log2vis' => 2, + 'fscanf' => 2, + 'fseek' => 2, + 'fsockopen' => 2, + 'fstat' => 2, + 'ftell' => 2, + 'ftok' => 2, + 'ftp_alloc' => 2, + 'ftp_cdup' => 2, + 'ftp_chdir' => 2, + 'ftp_chmod' => 2, + 'ftp_close' => 2, + 'ftp_connect' => 2, + 'ftp_delete' => 2, + 'ftp_exec' => 2, + 'ftp_fget' => 2, + 'ftp_fput' => 2, + 'ftp_get' => 2, + 'ftp_get_option' => 2, + 'ftp_login' => 2, + 'ftp_mdtm' => 2, + 'ftp_mkdir' => 2, + 'ftp_nb_continue' => 2, + 'ftp_nb_fget' => 2, + 'ftp_nb_fput' => 2, + 'ftp_nb_get' => 2, + 'ftp_nb_put' => 2, + 'ftp_nlist' => 2, + 'ftp_pasv' => 2, + 'ftp_put' => 2, + 'ftp_pwd' => 2, + 'ftp_quit' => 2, + 'ftp_raw' => 2, + 'ftp_rawlist' => 2, + 'ftp_rename' => 2, + 'ftp_rmdir' => 2, + 'ftp_set_option' => 2, + 'ftp_site' => 2, + 'ftp_size' => 2, + 'ftp_ssl_connect' => 2, + 'ftp_systype' => 2, + 'ftruncate' => 2, + 'func_get_arg' => 2, + 'func_get_args' => 2, + 'func_num_args' => 2, + 'function_exists' => 2, + 'fwrite' => 2, + 'gc_collect_cycles' => 2, + 'gc_disable' => 2, + 'gc_enable' => 2, + 'gc_enabled' => 2, + 'gd_info' => 2, + 'get_browser' => 2, + 'get_called_class' => 2, + 'get_cfg_var' => 2, + 'get_class' => 2, + 'get_class_methods' => 2, + 'get_class_vars' => 2, + 'get_current_user' => 2, + 'get_declared_classes' => 2, + 'get_declared_interfaces' => 2, + 'get_declared_traits' => 2, + 'get_defined_constants' => 2, + 'get_defined_functions' => 2, + 'get_defined_vars' => 2, + 'get_extension_funcs' => 2, + 'get_headers' => 2, + 'get_html_translation_table' => 2, + 'get_include_path' => 2, + 'get_included_files' => 2, + 'get_loaded_extensions' => 2, + 'get_magic_quotes_gpc' => 2, + 'get_magic_quotes_runtime' => 2, + 'get_meta_tags' => 2, + 'get_object_vars' => 2, + 'get_parent_class' => 2, + 'get_required_files' => 2, + 'get_resource_type' => 2, + 'getallheaders' => 2, + 'getcwd' => 2, + 'getdate' => 2, + 'getenv' => 2, + 'gethostbyaddr' => 2, + 'gethostbyname' => 2, + 'gethostbynamel' => 2, + 'gethostname' => 2, + 'getimagesize' => 2, + 'getimagesizefromstring' => 2, + 'getlastmod' => 2, + 'getmxrr' => 2, + 'getmygid' => 2, + 'getmyinode' => 2, + 'getmypid' => 2, + 'getmyuid' => 2, + 'getopt' => 2, + 'getprotobyname' => 2, + 'getprotobynumber' => 2, + 'getrandmax' => 2, + 'getrusage' => 2, + 'getservbyname' => 2, + 'getservbyport' => 2, + 'gettext' => 2, + 'gettimeofday' => 2, + 'gettype' => 2, + 'glob' => 2, + 'gmdate' => 2, + 'gmmktime' => 2, + 'gmp_abs' => 2, + 'gmp_add' => 2, + 'gmp_and' => 2, + 'gmp_clrbit' => 2, + 'gmp_cmp' => 2, + 'gmp_com' => 2, + 'gmp_div' => 2, + 'gmp_div_q' => 2, + 'gmp_div_qr' => 2, + 'gmp_div_r' => 2, + 'gmp_divexact' => 2, + 'gmp_fact' => 2, + 'gmp_gcd' => 2, + 'gmp_gcdext' => 2, + 'gmp_hamdist' => 2, + 'gmp_init' => 2, + 'gmp_intval' => 2, + 'gmp_invert' => 2, + 'gmp_jacobi' => 2, + 'gmp_legendre' => 2, + 'gmp_mod' => 2, + 'gmp_mul' => 2, + 'gmp_neg' => 2, + 'gmp_nextprime' => 2, + 'gmp_or' => 2, + 'gmp_perfect_square' => 2, + 'gmp_popcount' => 2, + 'gmp_pow' => 2, + 'gmp_powm' => 2, + 'gmp_prob_prime' => 2, + 'gmp_random' => 2, + 'gmp_scan0' => 2, + 'gmp_scan1' => 2, + 'gmp_setbit' => 2, + 'gmp_sign' => 2, + 'gmp_sqrt' => 2, + 'gmp_sqrtrem' => 2, + 'gmp_strval' => 2, + 'gmp_sub' => 2, + 'gmp_xor' => 2, + 'gmstrftime' => 2, + 'gopher_parsedir' => 2, + 'grapheme_extract' => 2, + 'grapheme_stripos' => 2, + 'grapheme_stristr' => 2, + 'grapheme_strlen' => 2, + 'grapheme_strpos' => 2, + 'grapheme_strripos' => 2, + 'grapheme_strrpos' => 2, + 'grapheme_strstr' => 2, + 'grapheme_substr' => 2, + 'gregoriantojd' => 2, + 'gzclose' => 2, + 'gzcompress' => 2, + 'gzdecode' => 2, + 'gzdeflate' => 2, + 'gzencode' => 2, + 'gzeof' => 2, + 'gzfile' => 2, + 'gzgetc' => 2, + 'gzgets' => 2, + 'gzgetss' => 2, + 'gzinflate' => 2, + 'gzopen' => 2, + 'gzpassthru' => 2, + 'gzputs' => 2, + 'gzread' => 2, + 'gzrewind' => 2, + 'gzseek' => 2, + 'gztell' => 2, + 'gzuncompress' => 2, + 'gzwrite' => 2, + 'hash' => 2, + 'hash_algos' => 2, + 'hash_copy' => 2, + 'hash_file' => 2, + 'hash_final' => 2, + 'hash_hmac' => 2, + 'hash_hmac_file' => 2, + 'hash_init' => 2, + 'hash_update' => 2, + 'hash_update_file' => 2, + 'hash_update_stream' => 2, + 'header' => 2, + 'header_register_callback' => 2, + 'header_remove' => 2, + 'headers_list' => 2, + 'headers_sent' => 2, + 'hebrev' => 2, + 'hebrevc' => 2, + 'hex2bin' => 2, + 'hexdec' => 2, + 'highlight_file' => 2, + 'highlight_string' => 2, + 'html_doc' => 2, + 'html_doc_file' => 2, + 'html_entity_decode' => 2, + 'htmlentities' => 2, + 'htmlspecialchars' => 2, + 'htmlspecialchars_decode' => 2, + 'http_build_cookie' => 2, + 'http_build_query' => 2, + 'http_build_str' => 2, + 'http_build_url' => 2, + 'http_cache_etag' => 2, + 'http_cache_last_modified' => 2, + 'http_chunked_decode' => 2, + 'http_date' => 2, + 'http_deflate' => 2, + 'http_get' => 2, + 'http_get_request_body' => 2, + 'http_get_request_body_stream' => 2, + 'http_get_request_headers' => 2, + 'http_head' => 2, + 'http_inflate' => 2, + 'http_match_etag' => 2, + 'http_match_modified' => 2, + 'http_match_request_header' => 2, + 'http_negotiate' => 2, + 'http_negotiate_charset' => 2, + 'http_negotiate_content_type' => 2, + 'http_negotiate_language' => 2, + 'http_parse_cookie' => 2, + 'http_parse_headers' => 2, + 'http_parse_message' => 2, + 'http_parse_params' => 2, + 'http_persistent_handles_clean' => 2, + 'http_persistent_handles_count' => 2, + 'http_persistent_handles_ident' => 2, + 'http_post_data' => 2, + 'http_post_fields' => 2, + 'http_put_data' => 2, + 'http_put_file' => 2, + 'http_put_stream' => 2, + 'http_redirect' => 2, + 'http_request' => 2, + 'http_request_body_encode' => 2, + 'http_request_method_exists' => 2, + 'http_request_method_name' => 2, + 'http_request_method_register' => 2, + 'http_request_method_unregister' => 2, + 'http_response_code' => 2, + 'http_send_content_disposition' => 2, + 'http_send_content_type' => 2, + 'http_send_data' => 2, + 'http_send_file' => 2, + 'http_send_last_modified' => 2, + 'http_send_status' => 2, + 'http_send_stream' => 2, + 'http_support' => 2, + 'http_throttle' => 2, + 'hw_array2objrec' => 2, + 'hw_children' => 2, + 'hw_childrenobj' => 2, + 'hw_close' => 2, + 'hw_connect' => 2, + 'hw_cp' => 2, + 'hw_deleteobject' => 2, + 'hw_docbyanchor' => 2, + 'hw_docbyanchorobj' => 2, + 'hw_documentattributes' => 2, + 'hw_documentbodytag' => 2, + 'hw_documentcontent' => 2, + 'hw_documentsetcontent' => 2, + 'hw_documentsize' => 2, + 'hw_edittext' => 2, + 'hw_error' => 2, + 'hw_errormsg' => 2, + 'hw_free_document' => 2, + 'hw_getanchors' => 2, + 'hw_getanchorsobj' => 2, + 'hw_getandlock' => 2, + 'hw_getchildcoll' => 2, + 'hw_getchildcollobj' => 2, + 'hw_getchilddoccoll' => 2, + 'hw_getchilddoccollobj' => 2, + 'hw_getobject' => 2, + 'hw_getobjectbyquery' => 2, + 'hw_getobjectbyquerycoll' => 2, + 'hw_getobjectbyquerycollobj' => 2, + 'hw_getobjectbyqueryobj' => 2, + 'hw_getparents' => 2, + 'hw_getparentsobj' => 2, + 'hw_getremote' => 2, + 'hw_getremotechildren' => 2, + 'hw_getsrcbydestobj' => 2, + 'hw_gettext' => 2, + 'hw_identify' => 2, + 'hw_incollections' => 2, + 'hw_info' => 2, + 'hw_inscoll' => 2, + 'hw_insdoc' => 2, + 'hw_insertdocument' => 2, + 'hw_insertobject' => 2, + 'hw_mapid' => 2, + 'hw_modifyobject' => 2, + 'hw_mv' => 2, + 'hw_new_document' => 2, + 'hw_objrec2array' => 2, + 'hw_outputdocument' => 2, + 'hw_pconnect' => 2, + 'hw_pipedocument' => 2, + 'hw_root' => 2, + 'hw_unlock' => 2, + 'hw_username' => 2, + 'hw_who' => 2, + 'hypot' => 2, + 'i18n_convert' => 2, + 'i18n_discover_encoding' => 2, + 'i18n_http_input' => 2, + 'i18n_http_output' => 2, + 'i18n_internal_encoding' => 2, + 'i18n_ja_jp_hantozen' => 2, + 'i18n_mime_header_decode' => 2, + 'i18n_mime_header_encode' => 2, + 'ibase_add_user' => 2, + 'ibase_affected_rows' => 2, + 'ibase_backup' => 2, + 'ibase_bind' => 2, + 'ibase_blob_add' => 2, + 'ibase_blob_cancel' => 2, + 'ibase_blob_close' => 2, + 'ibase_blob_create' => 2, + 'ibase_blob_echo' => 2, + 'ibase_blob_get' => 2, + 'ibase_blob_import' => 2, + 'ibase_blob_info' => 2, + 'ibase_blob_open' => 2, + 'ibase_close' => 2, + 'ibase_commit' => 2, + 'ibase_commit_ret' => 2, + 'ibase_connect' => 2, + 'ibase_db_info' => 2, + 'ibase_delete_user' => 2, + 'ibase_drop_db' => 2, + 'ibase_errcode' => 2, + 'ibase_errmsg' => 2, + 'ibase_execute' => 2, + 'ibase_fetch_assoc' => 2, + 'ibase_fetch_object' => 2, + 'ibase_fetch_row' => 2, + 'ibase_field_info' => 2, + 'ibase_free_event_handler' => 2, + 'ibase_free_query' => 2, + 'ibase_free_result' => 2, + 'ibase_gen_id' => 2, + 'ibase_maintain_db' => 2, + 'ibase_modify_user' => 2, + 'ibase_name_result' => 2, + 'ibase_num_fields' => 2, + 'ibase_num_params' => 2, + 'ibase_param_info' => 2, + 'ibase_pconnect' => 2, + 'ibase_prepare' => 2, + 'ibase_query' => 2, + 'ibase_restore' => 2, + 'ibase_rollback' => 2, + 'ibase_rollback_ret' => 2, + 'ibase_server_info' => 2, + 'ibase_service_attach' => 2, + 'ibase_service_detach' => 2, + 'ibase_set_event_handler' => 2, + 'ibase_timefmt' => 2, + 'ibase_trans' => 2, + 'ibase_wait_event' => 2, + 'iconv' => 2, + 'iconv_get_encoding' => 2, + 'iconv_mime_decode' => 2, + 'iconv_mime_decode_headers' => 2, + 'iconv_mime_encode' => 2, + 'iconv_set_encoding' => 2, + 'iconv_strlen' => 2, + 'iconv_strpos' => 2, + 'iconv_strrpos' => 2, + 'iconv_substr' => 2, + 'id3_get_frame_long_name' => 2, + 'id3_get_frame_short_name' => 2, + 'id3_get_genre_id' => 2, + 'id3_get_genre_list' => 2, + 'id3_get_genre_name' => 2, + 'id3_get_tag' => 2, + 'id3_get_version' => 2, + 'id3_remove_tag' => 2, + 'id3_set_tag' => 2, + 'idate' => 2, + 'idn_to_ascii' => 2, + 'idn_to_utf8' => 2, + 'ifx_affected_rows' => 2, + 'ifx_blobinfile_mode' => 2, + 'ifx_byteasvarchar' => 2, + 'ifx_close' => 2, + 'ifx_connect' => 2, + 'ifx_copy_blob' => 2, + 'ifx_create_blob' => 2, + 'ifx_create_char' => 2, + 'ifx_do' => 2, + 'ifx_error' => 2, + 'ifx_errormsg' => 2, + 'ifx_fetch_row' => 2, + 'ifx_fieldproperties' => 2, + 'ifx_fieldtypes' => 2, + 'ifx_free_blob' => 2, + 'ifx_free_char' => 2, + 'ifx_free_result' => 2, + 'ifx_free_slob' => 2, + 'ifx_get_blob' => 2, + 'ifx_get_char' => 2, + 'ifx_getsqlca' => 2, + 'ifx_htmltbl_result' => 2, + 'ifx_nullformat' => 2, + 'ifx_num_fields' => 2, + 'ifx_num_rows' => 2, + 'ifx_pconnect' => 2, + 'ifx_prepare' => 2, + 'ifx_query' => 2, + 'ifx_textasvarchar' => 2, + 'ifx_update_blob' => 2, + 'ifx_update_char' => 2, + 'ifxus_close_slob' => 2, + 'ifxus_create_slob' => 2, + 'ifxus_open_slob' => 2, + 'ifxus_read_slob' => 2, + 'ifxus_seek_slob' => 2, + 'ifxus_tell_slob' => 2, + 'ifxus_write_slob' => 2, + 'ignore_user_abort' => 2, + 'image2wbmp' => 2, + 'image_type_to_extension' => 2, + 'image_type_to_mime_type' => 2, + 'imagealphablending' => 2, + 'imageantialias' => 2, + 'imagearc' => 2, + 'imagechar' => 2, + 'imagecharup' => 2, + 'imagecolorallocate' => 2, + 'imagecolorallocatealpha' => 2, + 'imagecolorat' => 2, + 'imagecolorclosest' => 2, + 'imagecolorclosestalpha' => 2, + 'imagecolorclosesthwb' => 2, + 'imagecolordeallocate' => 2, + 'imagecolorexact' => 2, + 'imagecolorexactalpha' => 2, + 'imagecolormatch' => 2, + 'imagecolorresolve' => 2, + 'imagecolorresolvealpha' => 2, + 'imagecolorset' => 2, + 'imagecolorsforindex' => 2, + 'imagecolorstotal' => 2, + 'imagecolortransparent' => 2, + 'imageconvolution' => 2, + 'imagecopy' => 2, + 'imagecopymerge' => 2, + 'imagecopymergegray' => 2, + 'imagecopyresampled' => 2, + 'imagecopyresized' => 2, + 'imagecreate' => 2, + 'imagecreatefromgd' => 2, + 'imagecreatefromgd2' => 2, + 'imagecreatefromgd2part' => 2, + 'imagecreatefromgif' => 2, + 'imagecreatefromjpeg' => 2, + 'imagecreatefrompng' => 2, + 'imagecreatefromstring' => 2, + 'imagecreatefromwbmp' => 2, + 'imagecreatefromxbm' => 2, + 'imagecreatetruecolor' => 2, + 'imagedashedline' => 2, + 'imagedestroy' => 2, + 'imageellipse' => 2, + 'imagefill' => 2, + 'imagefilledarc' => 2, + 'imagefilledellipse' => 2, + 'imagefilledpolygon' => 2, + 'imagefilledrectangle' => 2, + 'imagefilltoborder' => 2, + 'imagefilter' => 2, + 'imagefontheight' => 2, + 'imagefontwidth' => 2, + 'imageftbbox' => 2, + 'imagefttext' => 2, + 'imagegammacorrect' => 2, + 'imagegd' => 2, + 'imagegd2' => 2, + 'imagegif' => 2, + 'imagegrabscreen' => 2, + 'imagegrabwindow' => 2, + 'imageinterlace' => 2, + 'imageistruecolor' => 2, + 'imagejpeg' => 2, + 'imagelayereffect' => 2, + 'imageline' => 2, + 'imageloadfont' => 2, + 'imagepalettecopy' => 2, + 'imagepng' => 2, + 'imagepolygon' => 2, + 'imagepsbbox' => 2, + 'imagepsencodefont' => 2, + 'imagepsextendfont' => 2, + 'imagepsfreefont' => 2, + 'imagepsloadfont' => 2, + 'imagepsslantfont' => 2, + 'imagepstext' => 2, + 'imagerectangle' => 2, + 'imagerotate' => 2, + 'imagesavealpha' => 2, + 'imagesetbrush' => 2, + 'imagesetpixel' => 2, + 'imagesetstyle' => 2, + 'imagesetthickness' => 2, + 'imagesettile' => 2, + 'imagestring' => 2, + 'imagestringup' => 2, + 'imagesx' => 2, + 'imagesy' => 2, + 'imagetruecolortopalette' => 2, + 'imagettfbbox' => 2, + 'imagettftext' => 2, + 'imagetypes' => 2, + 'imagewbmp' => 2, + 'imagexbm' => 2, + 'imap_8bit' => 2, + 'imap_alerts' => 2, + 'imap_append' => 2, + 'imap_base64' => 2, + 'imap_binary' => 2, + 'imap_body' => 2, + 'imap_bodystruct' => 2, + 'imap_check' => 2, + 'imap_clearflag_full' => 2, + 'imap_close' => 2, + 'imap_create' => 2, + 'imap_createmailbox' => 2, + 'imap_delete' => 2, + 'imap_deletemailbox' => 2, + 'imap_errors' => 2, + 'imap_expunge' => 2, + 'imap_fetch_overview' => 2, + 'imap_fetchbody' => 2, + 'imap_fetchheader' => 2, + 'imap_fetchmime' => 2, + 'imap_fetchstructure' => 2, + 'imap_fetchtext' => 2, + 'imap_gc' => 2, + 'imap_get_quota' => 2, + 'imap_get_quotaroot' => 2, + 'imap_getacl' => 2, + 'imap_getmailboxes' => 2, + 'imap_getsubscribed' => 2, + 'imap_header' => 2, + 'imap_headerinfo' => 2, + 'imap_headers' => 2, + 'imap_last_error' => 2, + 'imap_list' => 2, + 'imap_listmailbox' => 2, + 'imap_listscan' => 2, + 'imap_listsubscribed' => 2, + 'imap_lsub' => 2, + 'imap_mail' => 2, + 'imap_mail_compose' => 2, + 'imap_mail_copy' => 2, + 'imap_mail_move' => 2, + 'imap_mailboxmsginfo' => 2, + 'imap_mime_header_decode' => 2, + 'imap_msgno' => 2, + 'imap_mutf7_to_utf8' => 2, + 'imap_num_msg' => 2, + 'imap_num_recent' => 2, + 'imap_open' => 2, + 'imap_ping' => 2, + 'imap_qprint' => 2, + 'imap_rename' => 2, + 'imap_renamemailbox' => 2, + 'imap_reopen' => 2, + 'imap_rfc822_parse_adrlist' => 2, + 'imap_rfc822_parse_headers' => 2, + 'imap_rfc822_write_address' => 2, + 'imap_savebody' => 2, + 'imap_scan' => 2, + 'imap_scanmailbox' => 2, + 'imap_search' => 2, + 'imap_set_quota' => 2, + 'imap_setacl' => 2, + 'imap_setflag_full' => 2, + 'imap_sort' => 2, + 'imap_status' => 2, + 'imap_subscribe' => 2, + 'imap_thread' => 2, + 'imap_timeout' => 2, + 'imap_uid' => 2, + 'imap_undelete' => 2, + 'imap_unsubscribe' => 2, + 'imap_utf7_decode' => 2, + 'imap_utf7_encode' => 2, + 'imap_utf8' => 2, + 'imap_utf8_to_mutf7' => 2, + 'implode' => 2, + 'import_request_variables' => 2, + 'in_array' => 2, + 'inet_ntop' => 2, + 'inet_pton' => 2, + 'ini_alter' => 2, + 'ini_get' => 2, + 'ini_get_all' => 2, + 'ini_restore' => 2, + 'ini_set' => 2, + 'interface_exists' => 2, + 'intl_error_name' => 2, + 'intl_get_error_code' => 2, + 'intl_get_error_message' => 2, + 'intl_is_failure' => 2, + 'intval' => 2, + 'ip2long' => 2, + 'iptcembed' => 2, + 'iptcparse' => 2, + 'is_a' => 2, + 'is_array' => 2, + 'is_bool' => 2, + 'is_callable' => 2, + 'is_dir' => 2, + 'is_double' => 2, + 'is_executable' => 2, + 'is_file' => 2, + 'is_finite' => 2, + 'is_float' => 2, + 'is_infinite' => 2, + 'is_int' => 2, + 'is_integer' => 2, + 'is_link' => 2, + 'is_long' => 2, + 'is_nan' => 2, + 'is_null' => 2, + 'is_numeric' => 2, + 'is_object' => 2, + 'is_readable' => 2, + 'is_real' => 2, + 'is_resource' => 2, + 'is_scalar' => 2, + 'is_soap_fault' => 2, + 'is_string' => 2, + 'is_subclass_of' => 2, + 'is_uploaded_file' => 2, + 'is_writable' => 2, + 'is_writeable' => 2, + 'iterator_apply' => 2, + 'iterator_count' => 2, + 'iterator_to_array' => 2, + 'java_last_exception_clear' => 2, + 'java_last_exception_get' => 2, + 'jddayofweek' => 2, + 'jdmonthname' => 2, + 'jdtofrench' => 2, + 'jdtogregorian' => 2, + 'jdtojewish' => 2, + 'jdtojulian' => 2, + 'jdtounix' => 2, + 'jewishtojd' => 2, + 'join' => 2, + 'jpeg2wbmp' => 2, + 'json_decode' => 2, + 'json_encode' => 2, + 'json_last_error' => 2, + 'juliantojd' => 2, + 'key' => 2, + 'key_exists' => 2, + 'krsort' => 2, + 'ksort' => 2, + 'lcfirst' => 2, + 'lcg_value' => 2, + 'ldap_add' => 2, + 'ldap_bind' => 2, + 'ldap_close' => 2, + 'ldap_compare' => 2, + 'ldap_connect' => 2, + 'ldap_count_entries' => 2, + 'ldap_delete' => 2, + 'ldap_dn2ufn' => 2, + 'ldap_err2str' => 2, + 'ldap_errno' => 2, + 'ldap_error' => 2, + 'ldap_explode_dn' => 2, + 'ldap_first_attribute' => 2, + 'ldap_first_entry' => 2, + 'ldap_first_reference' => 2, + 'ldap_free_result' => 2, + 'ldap_get_attributes' => 2, + 'ldap_get_dn' => 2, + 'ldap_get_entries' => 2, + 'ldap_get_option' => 2, + 'ldap_get_values' => 2, + 'ldap_get_values_len' => 2, + 'ldap_list' => 2, + 'ldap_mod_add' => 2, + 'ldap_mod_del' => 2, + 'ldap_mod_replace' => 2, + 'ldap_modify' => 2, + 'ldap_next_attribute' => 2, + 'ldap_next_entry' => 2, + 'ldap_next_reference' => 2, + 'ldap_parse_reference' => 2, + 'ldap_parse_result' => 2, + 'ldap_read' => 2, + 'ldap_rename' => 2, + 'ldap_search' => 2, + 'ldap_set_option' => 2, + 'ldap_sort' => 2, + 'ldap_start_tls' => 2, + 'ldap_unbind' => 2, + 'leak' => 2, + 'levenshtein' => 2, + 'libxml_clear_errors' => 2, + 'libxml_disable_entity_loader' => 2, + 'libxml_get_errors' => 2, + 'libxml_get_last_error' => 2, + 'libxml_set_streams_context' => 2, + 'libxml_use_internal_errors' => 2, + 'link' => 2, + 'linkinfo' => 2, + 'locale_accept_from_http' => 2, + 'locale_canonicalize' => 2, + 'locale_compose' => 2, + 'locale_filter_matches' => 2, + 'locale_get_all_variants' => 2, + 'locale_get_default' => 2, + 'locale_get_display_language' => 2, + 'locale_get_display_name' => 2, + 'locale_get_display_region' => 2, + 'locale_get_display_script' => 2, + 'locale_get_display_variant' => 2, + 'locale_get_keywords' => 2, + 'locale_get_primary_language' => 2, + 'locale_get_region' => 2, + 'locale_get_script' => 2, + 'locale_lookup' => 2, + 'locale_parse' => 2, + 'locale_set_default' => 2, + 'localeconv' => 2, + 'localtime' => 2, + 'log' => 2, + 'log10' => 2, + 'log1p' => 2, + 'long2ip' => 2, + 'lstat' => 2, + 'ltrim' => 2, + 'lzf_compress' => 2, + 'lzf_decompress' => 2, + 'lzf_optimized_for' => 2, + 'magic_quotes_runtime' => 2, + 'mail' => 2, + 'max' => 2, + 'mb_check_encoding' => 2, + 'mb_convert_case' => 2, + 'mb_convert_encoding' => 2, + 'mb_convert_kana' => 2, + 'mb_convert_variables' => 2, + 'mb_decode_mimeheader' => 2, + 'mb_decode_numericentity' => 2, + 'mb_detect_encoding' => 2, + 'mb_detect_order' => 2, + 'mb_encode_mimeheader' => 2, + 'mb_encode_numericentity' => 2, + 'mb_encoding_aliases' => 2, + 'mb_ereg' => 2, + 'mb_ereg_match' => 2, + 'mb_ereg_replace' => 2, + 'mb_ereg_search' => 2, + 'mb_ereg_search_getpos' => 2, + 'mb_ereg_search_getregs' => 2, + 'mb_ereg_search_init' => 2, + 'mb_ereg_search_pos' => 2, + 'mb_ereg_search_regs' => 2, + 'mb_ereg_search_setpos' => 2, + 'mb_eregi' => 2, + 'mb_eregi_replace' => 2, + 'mb_get_info' => 2, + 'mb_http_input' => 2, + 'mb_http_output' => 2, + 'mb_internal_encoding' => 2, + 'mb_language' => 2, + 'mb_list_encodings' => 2, + 'mb_output_handler' => 2, + 'mb_parse_str' => 2, + 'mb_preferred_mime_name' => 2, + 'mb_regex_encoding' => 2, + 'mb_regex_set_options' => 2, + 'mb_send_mail' => 2, + 'mb_split' => 2, + 'mb_strcut' => 2, + 'mb_strimwidth' => 2, + 'mb_stripos' => 2, + 'mb_stristr' => 2, + 'mb_strlen' => 2, + 'mb_strpos' => 2, + 'mb_strrchr' => 2, + 'mb_strrichr' => 2, + 'mb_strripos' => 2, + 'mb_strrpos' => 2, + 'mb_strstr' => 2, + 'mb_strtolower' => 2, + 'mb_strtoupper' => 2, + 'mb_strwidth' => 2, + 'mb_substitute_character' => 2, + 'mb_substr' => 2, + 'mb_substr_count' => 2, + 'mbereg' => 2, + 'mbereg_match' => 2, + 'mbereg_replace' => 2, + 'mbereg_search' => 2, + 'mbereg_search_getpos' => 2, + 'mbereg_search_getregs' => 2, + 'mbereg_search_init' => 2, + 'mbereg_search_pos' => 2, + 'mbereg_search_regs' => 2, + 'mbereg_search_setpos' => 2, + 'mberegi' => 2, + 'mberegi_replace' => 2, + 'mbregex_encoding' => 2, + 'mbsplit' => 2, + 'mbstrcut' => 2, + 'mbstrlen' => 2, + 'mbstrpos' => 2, + 'mbstrrpos' => 2, + 'mbsubstr' => 2, + 'mcal_close' => 2, + 'mcal_date_compare' => 2, + 'mcal_date_valid' => 2, + 'mcal_day_of_week' => 2, + 'mcal_day_of_year' => 2, + 'mcal_days_in_month' => 2, + 'mcal_delete_event' => 2, + 'mcal_event_init' => 2, + 'mcal_event_set_alarm' => 2, + 'mcal_event_set_category' => 2, + 'mcal_event_set_class' => 2, + 'mcal_event_set_description' => 2, + 'mcal_event_set_end' => 2, + 'mcal_event_set_recur_daily' => 2, + 'mcal_event_set_recur_monthly_mday' => 2, + 'mcal_event_set_recur_monthly_wday' => 2, + 'mcal_event_set_recur_weekly' => 2, + 'mcal_event_set_recur_yearly' => 2, + 'mcal_event_set_start' => 2, + 'mcal_event_set_title' => 2, + 'mcal_fetch_current_stream_event' => 2, + 'mcal_fetch_event' => 2, + 'mcal_is_leap_year' => 2, + 'mcal_list_alarms' => 2, + 'mcal_list_events' => 2, + 'mcal_next_recurrence' => 2, + 'mcal_open' => 2, + 'mcal_snooze' => 2, + 'mcal_store_event' => 2, + 'mcal_time_valid' => 2, + 'mcrypt_cbc' => 2, + 'mcrypt_cfb' => 2, + 'mcrypt_create_iv' => 2, + 'mcrypt_decrypt' => 2, + 'mcrypt_ecb' => 2, + 'mcrypt_enc_get_algorithms_name' => 2, + 'mcrypt_enc_get_block_size' => 2, + 'mcrypt_enc_get_iv_size' => 2, + 'mcrypt_enc_get_key_size' => 2, + 'mcrypt_enc_get_modes_name' => 2, + 'mcrypt_enc_get_supported_key_sizes' => 2, + 'mcrypt_enc_is_block_algorithm' => 2, + 'mcrypt_enc_is_block_algorithm_mode' => 2, + 'mcrypt_enc_is_block_mode' => 2, + 'mcrypt_enc_self_test' => 2, + 'mcrypt_encrypt' => 2, + 'mcrypt_generic' => 2, + 'mcrypt_generic_deinit' => 2, + 'mcrypt_generic_end' => 2, + 'mcrypt_generic_init' => 2, + 'mcrypt_get_block_size' => 2, + 'mcrypt_get_cipher_name' => 2, + 'mcrypt_get_iv_size' => 2, + 'mcrypt_get_key_size' => 2, + 'mcrypt_list_algorithms' => 2, + 'mcrypt_list_modes' => 2, + 'mcrypt_module_close' => 2, + 'mcrypt_module_get_algo_block_size' => 2, + 'mcrypt_module_get_algo_key_size' => 2, + 'mcrypt_module_get_supported_key_sizes' => 2, + 'mcrypt_module_is_block_algorithm' => 2, + 'mcrypt_module_is_block_algorithm_mode' => 2, + 'mcrypt_module_is_block_mode' => 2, + 'mcrypt_module_open' => 2, + 'mcrypt_module_self_test' => 2, + 'mcrypt_ofb' => 2, + 'md5' => 2, + 'md5_file' => 2, + 'mdecrypt_generic' => 2, + 'memcache_add' => 2, + 'memcache_add_server' => 2, + 'memcache_close' => 2, + 'memcache_connect' => 2, + 'memcache_debug' => 2, + 'memcache_decrement' => 2, + 'memcache_delete' => 2, + 'memcache_flush' => 2, + 'memcache_get' => 2, + 'memcache_get_extended_stats' => 2, + 'memcache_get_server_status' => 2, + 'memcache_get_stats' => 2, + 'memcache_get_version' => 2, + 'memcache_increment' => 2, + 'memcache_pconnect' => 2, + 'memcache_replace' => 2, + 'memcache_set' => 2, + 'memcache_set_compress_threshold' => 2, + 'memcache_set_server_params' => 2, + 'memory_get_peak_usage' => 2, + 'memory_get_usage' => 2, + 'metaphone' => 2, + 'method_exists' => 2, + 'mhash' => 2, + 'mhash_count' => 2, + 'mhash_get_block_size' => 2, + 'mhash_get_hash_name' => 2, + 'mhash_keygen_s2k' => 2, + 'microtime' => 2, + 'mime_content_type' => 2, + 'min' => 2, + 'ming_keypress' => 2, + 'ming_setcubicthreshold' => 2, + 'ming_setscale' => 2, + 'ming_useconstants' => 2, + 'ming_useswfversion' => 2, + 'mkdir' => 2, + 'mktime' => 2, + 'modifiers' => 2, + 'money_format' => 2, + 'move_uploaded_file' => 2, + 'msgfmt_create' => 2, + 'msgfmt_format' => 2, + 'msgfmt_format_message' => 2, + 'msgfmt_get_error_code' => 2, + 'msgfmt_get_error_message' => 2, + 'msgfmt_get_locale' => 2, + 'msgfmt_get_pattern' => 2, + 'msgfmt_parse' => 2, + 'msgfmt_parse_message' => 2, + 'msgfmt_set_pattern' => 2, + 'msql' => 2, + 'msql_affected_rows' => 2, + 'msql_close' => 2, + 'msql_connect' => 2, + 'msql_create_db' => 2, + 'msql_createdb' => 2, + 'msql_data_seek' => 2, + 'msql_db_query' => 2, + 'msql_dbname' => 2, + 'msql_drop_db' => 2, + 'msql_dropdb' => 2, + 'msql_error' => 2, + 'msql_fetch_array' => 2, + 'msql_fetch_field' => 2, + 'msql_fetch_object' => 2, + 'msql_fetch_row' => 2, + 'msql_field_flags' => 2, + 'msql_field_len' => 2, + 'msql_field_name' => 2, + 'msql_field_seek' => 2, + 'msql_field_table' => 2, + 'msql_field_type' => 2, + 'msql_fieldflags' => 2, + 'msql_fieldlen' => 2, + 'msql_fieldname' => 2, + 'msql_fieldtable' => 2, + 'msql_fieldtype' => 2, + 'msql_free_result' => 2, + 'msql_freeresult' => 2, + 'msql_list_dbs' => 2, + 'msql_list_fields' => 2, + 'msql_list_tables' => 2, + 'msql_listdbs' => 2, + 'msql_listfields' => 2, + 'msql_listtables' => 2, + 'msql_num_fields' => 2, + 'msql_num_rows' => 2, + 'msql_numfields' => 2, + 'msql_numrows' => 2, + 'msql_pconnect' => 2, + 'msql_query' => 2, + 'msql_regcase' => 2, + 'msql_result' => 2, + 'msql_select_db' => 2, + 'msql_selectdb' => 2, + 'msql_tablename' => 2, + 'mssql_bind' => 2, + 'mssql_close' => 2, + 'mssql_connect' => 2, + 'mssql_data_seek' => 2, + 'mssql_execute' => 2, + 'mssql_fetch_array' => 2, + 'mssql_fetch_assoc' => 2, + 'mssql_fetch_batch' => 2, + 'mssql_fetch_field' => 2, + 'mssql_fetch_object' => 2, + 'mssql_fetch_row' => 2, + 'mssql_field_length' => 2, + 'mssql_field_name' => 2, + 'mssql_field_seek' => 2, + 'mssql_field_type' => 2, + 'mssql_free_result' => 2, + 'mssql_free_statement' => 2, + 'mssql_get_last_message' => 2, + 'mssql_guid_string' => 2, + 'mssql_init' => 2, + 'mssql_min_error_severity' => 2, + 'mssql_min_message_severity' => 2, + 'mssql_next_result' => 2, + 'mssql_num_fields' => 2, + 'mssql_num_rows' => 2, + 'mssql_pconnect' => 2, + 'mssql_query' => 2, + 'mssql_result' => 2, + 'mssql_rows_affected' => 2, + 'mssql_select_db' => 2, + 'mt_getrandmax' => 2, + 'mt_rand' => 2, + 'mt_srand' => 2, + 'mysql' => 2, + 'mysql_affected_rows' => 2, + 'mysql_change_user' => 2, + 'mysql_client_encoding' => 2, + 'mysql_close' => 2, + 'mysql_connect' => 2, + 'mysql_create_db' => 2, + 'mysql_createdb' => 2, + 'mysql_data_seek' => 2, + 'mysql_db_name' => 2, + 'mysql_db_query' => 2, + 'mysql_dbname' => 2, + 'mysql_drop_db' => 2, + 'mysql_dropdb' => 2, + 'mysql_errno' => 2, + 'mysql_error' => 2, + 'mysql_escape_string' => 2, + 'mysql_fetch_array' => 2, + 'mysql_fetch_assoc' => 2, + 'mysql_fetch_field' => 2, + 'mysql_fetch_lengths' => 2, + 'mysql_fetch_object' => 2, + 'mysql_fetch_row' => 2, + 'mysql_field_flags' => 2, + 'mysql_field_len' => 2, + 'mysql_field_name' => 2, + 'mysql_field_seek' => 2, + 'mysql_field_table' => 2, + 'mysql_field_type' => 2, + 'mysql_fieldflags' => 2, + 'mysql_fieldlen' => 2, + 'mysql_fieldname' => 2, + 'mysql_fieldtable' => 2, + 'mysql_fieldtype' => 2, + 'mysql_free_result' => 2, + 'mysql_freeresult' => 2, + 'mysql_get_client_info' => 2, + 'mysql_get_host_info' => 2, + 'mysql_get_proto_info' => 2, + 'mysql_get_server_info' => 2, + 'mysql_info' => 2, + 'mysql_insert_id' => 2, + 'mysql_list_dbs' => 2, + 'mysql_list_fields' => 2, + 'mysql_list_processes' => 2, + 'mysql_list_tables' => 2, + 'mysql_listdbs' => 2, + 'mysql_listfields' => 2, + 'mysql_listtables' => 2, + 'mysql_num_fields' => 2, + 'mysql_num_rows' => 2, + 'mysql_numfields' => 2, + 'mysql_numrows' => 2, + 'mysql_pconnect' => 2, + 'mysql_ping' => 2, + 'mysql_query' => 2, + 'mysql_real_escape_string' => 2, + 'mysql_result' => 2, + 'mysql_select_db' => 2, + 'mysql_selectdb' => 2, + 'mysql_set_charset' => 2, + 'mysql_stat' => 2, + 'mysql_table_name' => 2, + 'mysql_tablename' => 2, + 'mysql_thread_id' => 2, + 'mysql_unbuffered_query' => 2, + 'mysqli_affected_rows' => 2, + 'mysqli_autocommit' => 2, + 'mysqli_bind_param' => 2, + 'mysqli_bind_result' => 2, + 'mysqli_change_user' => 2, + 'mysqli_character_set_name' => 2, + 'mysqli_client_encoding' => 2, + 'mysqli_close' => 2, + 'mysqli_commit' => 2, + 'mysqli_connect' => 2, + 'mysqli_connect_errno' => 2, + 'mysqli_connect_error' => 2, + 'mysqli_data_seek' => 2, + 'mysqli_debug' => 2, + 'mysqli_disable_reads_from_master' => 2, + 'mysqli_disable_rpl_parse' => 2, + 'mysqli_dump_debug_info' => 2, + 'mysqli_embedded_server_end' => 2, + 'mysqli_embedded_server_start' => 2, + 'mysqli_enable_reads_from_master' => 2, + 'mysqli_enable_rpl_parse' => 2, + 'mysqli_errno' => 2, + 'mysqli_error' => 2, + 'mysqli_escape_string' => 2, + 'mysqli_execute' => 2, + 'mysqli_fetch' => 2, + 'mysqli_fetch_all' => 2, + 'mysqli_fetch_array' => 2, + 'mysqli_fetch_assoc' => 2, + 'mysqli_fetch_field' => 2, + 'mysqli_fetch_field_direct' => 2, + 'mysqli_fetch_fields' => 2, + 'mysqli_fetch_lengths' => 2, + 'mysqli_fetch_object' => 2, + 'mysqli_fetch_row' => 2, + 'mysqli_field_count' => 2, + 'mysqli_field_seek' => 2, + 'mysqli_field_tell' => 2, + 'mysqli_free_result' => 2, + 'mysqli_get_cache_stats' => 2, + 'mysqli_get_charset' => 2, + 'mysqli_get_client_info' => 2, + 'mysqli_get_client_stats' => 2, + 'mysqli_get_client_version' => 2, + 'mysqli_get_connection_stats' => 2, + 'mysqli_get_host_info' => 2, + 'mysqli_get_metadata' => 2, + 'mysqli_get_proto_info' => 2, + 'mysqli_get_server_info' => 2, + 'mysqli_get_server_version' => 2, + 'mysqli_get_warnings' => 2, + 'mysqli_info' => 2, + 'mysqli_init' => 2, + 'mysqli_insert_id' => 2, + 'mysqli_kill' => 2, + 'mysqli_master_query' => 2, + 'mysqli_more_results' => 2, + 'mysqli_multi_query' => 2, + 'mysqli_next_result' => 2, + 'mysqli_num_fields' => 2, + 'mysqli_num_rows' => 2, + 'mysqli_options' => 2, + 'mysqli_param_count' => 2, + 'mysqli_ping' => 2, + 'mysqli_poll' => 2, + 'mysqli_prepare' => 2, + 'mysqli_query' => 2, + 'mysqli_real_connect' => 2, + 'mysqli_real_escape_string' => 2, + 'mysqli_real_query' => 2, + 'mysqli_reap_async_query' => 2, + 'mysqli_refresh' => 2, + 'mysqli_report' => 2, + 'mysqli_rollback' => 2, + 'mysqli_rpl_parse_enabled' => 2, + 'mysqli_rpl_probe' => 2, + 'mysqli_rpl_query_type' => 2, + 'mysqli_select_db' => 2, + 'mysqli_send_long_data' => 2, + 'mysqli_send_query' => 2, + 'mysqli_set_charset' => 2, + 'mysqli_set_local_infile_default' => 2, + 'mysqli_set_local_infile_handler' => 2, + 'mysqli_set_opt' => 2, + 'mysqli_slave_query' => 2, + 'mysqli_sqlstate' => 2, + 'mysqli_ssl_set' => 2, + 'mysqli_stat' => 2, + 'mysqli_stmt_affected_rows' => 2, + 'mysqli_stmt_attr_get' => 2, + 'mysqli_stmt_attr_set' => 2, + 'mysqli_stmt_bind_param' => 2, + 'mysqli_stmt_bind_result' => 2, + 'mysqli_stmt_close' => 2, + 'mysqli_stmt_data_seek' => 2, + 'mysqli_stmt_errno' => 2, + 'mysqli_stmt_error' => 2, + 'mysqli_stmt_execute' => 2, + 'mysqli_stmt_fetch' => 2, + 'mysqli_stmt_field_count' => 2, + 'mysqli_stmt_free_result' => 2, + 'mysqli_stmt_get_result' => 2, + 'mysqli_stmt_get_warnings' => 2, + 'mysqli_stmt_init' => 2, + 'mysqli_stmt_insert_id' => 2, + 'mysqli_stmt_more_results' => 2, + 'mysqli_stmt_next_result' => 2, + 'mysqli_stmt_num_rows' => 2, + 'mysqli_stmt_param_count' => 2, + 'mysqli_stmt_prepare' => 2, + 'mysqli_stmt_reset' => 2, + 'mysqli_stmt_result_metadata' => 2, + 'mysqli_stmt_send_long_data' => 2, + 'mysqli_stmt_sqlstate' => 2, + 'mysqli_stmt_store_result' => 2, + 'mysqli_store_result' => 2, + 'mysqli_thread_id' => 2, + 'mysqli_thread_safe' => 2, + 'mysqli_use_result' => 2, + 'mysqli_warning_count' => 2, + 'natcasesort' => 2, + 'natsort' => 2, + 'new_xmldoc' => 2, + 'next' => 2, + 'ngettext' => 2, + 'nl2br' => 2, + 'nl_langinfo' => 2, + 'normalizer_is_normalized' => 2, + 'normalizer_normalize' => 2, + 'ntuser_getdomaincontroller' => 2, + 'ntuser_getusergroups' => 2, + 'ntuser_getuserinfo' => 2, + 'ntuser_getuserlist' => 2, + 'number_format' => 2, + 'numfmt_create' => 2, + 'numfmt_format' => 2, + 'numfmt_format_currency' => 2, + 'numfmt_get_attribute' => 2, + 'numfmt_get_error_code' => 2, + 'numfmt_get_error_message' => 2, + 'numfmt_get_locale' => 2, + 'numfmt_get_pattern' => 2, + 'numfmt_get_symbol' => 2, + 'numfmt_get_text_attribute' => 2, + 'numfmt_parse' => 2, + 'numfmt_parse_currency' => 2, + 'numfmt_set_attribute' => 2, + 'numfmt_set_pattern' => 2, + 'numfmt_set_symbol' => 2, + 'numfmt_set_text_attribute' => 2, + 'ob_clean' => 2, + 'ob_deflatehandler' => 2, + 'ob_end_clean' => 2, + 'ob_end_flush' => 2, + 'ob_etaghandler' => 2, + 'ob_flush' => 2, + 'ob_get_clean' => 2, + 'ob_get_contents' => 2, + 'ob_get_flush' => 2, + 'ob_get_length' => 2, + 'ob_get_level' => 2, + 'ob_get_status' => 2, + 'ob_gzhandler' => 2, + 'ob_iconv_handler' => 2, + 'ob_implicit_flush' => 2, + 'ob_inflatehandler' => 2, + 'ob_list_handlers' => 2, + 'ob_start' => 2, + 'ob_tidyhandler' => 2, + 'ocibindbyname' => 2, + 'ocicolumnisnull' => 2, + 'ocicolumnname' => 2, + 'ocicolumnsize' => 2, + 'ocicolumntype' => 2, + 'ocicommit' => 2, + 'ocidefinebyname' => 2, + 'ocierror' => 2, + 'ociexecute' => 2, + 'ocifetch' => 2, + 'ocifetchinto' => 2, + 'ocifetchstatement' => 2, + 'ocifreecursor' => 2, + 'ocifreestatement' => 2, + 'ociinternaldebug' => 2, + 'ocilogoff' => 2, + 'ocilogon' => 2, + 'ocinewcursor' => 2, + 'ocinewdescriptor' => 2, + 'ocinlogon' => 2, + 'ocinumcols' => 2, + 'ociparse' => 2, + 'ociplogon' => 2, + 'ociresult' => 2, + 'ocirollback' => 2, + 'ocirowcount' => 2, + 'ociserverversion' => 2, + 'ocistatementtype' => 2, + 'octdec' => 2, + 'odbc_autocommit' => 2, + 'odbc_binmode' => 2, + 'odbc_close' => 2, + 'odbc_close_all' => 2, + 'odbc_columnprivileges' => 2, + 'odbc_columns' => 2, + 'odbc_commit' => 2, + 'odbc_connect' => 2, + 'odbc_cursor' => 2, + 'odbc_data_source' => 2, + 'odbc_do' => 2, + 'odbc_error' => 2, + 'odbc_errormsg' => 2, + 'odbc_exec' => 2, + 'odbc_execute' => 2, + 'odbc_fetch_array' => 2, + 'odbc_fetch_into' => 2, + 'odbc_fetch_object' => 2, + 'odbc_fetch_row' => 2, + 'odbc_field_len' => 2, + 'odbc_field_name' => 2, + 'odbc_field_num' => 2, + 'odbc_field_precision' => 2, + 'odbc_field_scale' => 2, + 'odbc_field_type' => 2, + 'odbc_foreignkeys' => 2, + 'odbc_free_result' => 2, + 'odbc_gettypeinfo' => 2, + 'odbc_longreadlen' => 2, + 'odbc_next_result' => 2, + 'odbc_num_fields' => 2, + 'odbc_num_rows' => 2, + 'odbc_pconnect' => 2, + 'odbc_prepare' => 2, + 'odbc_primarykeys' => 2, + 'odbc_procedurecolumns' => 2, + 'odbc_procedures' => 2, + 'odbc_result' => 2, + 'odbc_result_all' => 2, + 'odbc_rollback' => 2, + 'odbc_setoption' => 2, + 'odbc_specialcolumns' => 2, + 'odbc_statistics' => 2, + 'odbc_tableprivileges' => 2, + 'odbc_tables' => 2, + 'opendir' => 2, + 'openlog' => 2, + 'openssl_cipher_iv_length' => 2, + 'openssl_csr_export' => 2, + 'openssl_csr_export_to_file' => 2, + 'openssl_csr_get_public_key' => 2, + 'openssl_csr_get_subject' => 2, + 'openssl_csr_new' => 2, + 'openssl_csr_sign' => 2, + 'openssl_decrypt' => 2, + 'openssl_dh_compute_key' => 2, + 'openssl_digest' => 2, + 'openssl_encrypt' => 2, + 'openssl_error_string' => 2, + 'openssl_free_key' => 2, + 'openssl_get_cipher_methods' => 2, + 'openssl_get_md_methods' => 2, + 'openssl_get_privatekey' => 2, + 'openssl_get_publickey' => 2, + 'openssl_open' => 2, + 'openssl_pkcs12_export' => 2, + 'openssl_pkcs12_export_to_file' => 2, + 'openssl_pkcs12_read' => 2, + 'openssl_pkcs7_decrypt' => 2, + 'openssl_pkcs7_encrypt' => 2, + 'openssl_pkcs7_sign' => 2, + 'openssl_pkcs7_verify' => 2, + 'openssl_pkey_export' => 2, + 'openssl_pkey_export_to_file' => 2, + 'openssl_pkey_free' => 2, + 'openssl_pkey_get_details' => 2, + 'openssl_pkey_get_private' => 2, + 'openssl_pkey_get_public' => 2, + 'openssl_pkey_new' => 2, + 'openssl_private_decrypt' => 2, + 'openssl_private_encrypt' => 2, + 'openssl_public_decrypt' => 2, + 'openssl_public_encrypt' => 2, + 'openssl_random_pseudo_bytes' => 2, + 'openssl_seal' => 2, + 'openssl_sign' => 2, + 'openssl_verify' => 2, + 'openssl_x509_check_private_key' => 2, + 'openssl_x509_checkpurpose' => 2, + 'openssl_x509_export' => 2, + 'openssl_x509_export_to_file' => 2, + 'openssl_x509_free' => 2, + 'openssl_x509_parse' => 2, + 'openssl_x509_read' => 2, + 'ora_bind' => 2, + 'ora_close' => 2, + 'ora_columnname' => 2, + 'ora_columntype' => 2, + 'ora_commit' => 2, + 'ora_commitoff' => 2, + 'ora_commiton' => 2, + 'ora_error' => 2, + 'ora_errorcode' => 2, + 'ora_exec' => 2, + 'ora_fetch' => 2, + 'ora_getcolumn' => 2, + 'ora_logoff' => 2, + 'ora_logon' => 2, + 'ora_open' => 2, + 'ora_parse' => 2, + 'ora_rollback' => 2, + 'ord' => 2, + 'output_add_rewrite_var' => 2, + 'output_reset_rewrite_vars' => 2, + 'outputdebugstring' => 2, + 'overload' => 2, + 'pack' => 2, + 'parse_ini_file' => 2, + 'parse_ini_string' => 2, + 'parse_str' => 2, + 'parse_url' => 2, + 'parsekit_compile_file' => 2, + 'parsekit_compile_string' => 2, + 'parsekit_func_arginfo' => 2, + 'parsekit_opcode_flags' => 2, + 'parsekit_opcode_name' => 2, + 'passthru' => 2, + 'pathinfo' => 2, + 'pattern' => 2, + 'pclose' => 2, + 'pdf_add_annotation' => 2, + 'pdf_add_bookmark' => 2, + 'pdf_add_launchlink' => 2, + 'pdf_add_locallink' => 2, + 'pdf_add_nameddest' => 2, + 'pdf_add_note' => 2, + 'pdf_add_outline' => 2, + 'pdf_add_pdflink' => 2, + 'pdf_add_thumbnail' => 2, + 'pdf_add_weblink' => 2, + 'pdf_arc' => 2, + 'pdf_arcn' => 2, + 'pdf_attach_file' => 2, + 'pdf_begin_font' => 2, + 'pdf_begin_glyph' => 2, + 'pdf_begin_page' => 2, + 'pdf_begin_pattern' => 2, + 'pdf_begin_template' => 2, + 'pdf_circle' => 2, + 'pdf_clip' => 2, + 'pdf_close' => 2, + 'pdf_close_image' => 2, + 'pdf_close_pdi' => 2, + 'pdf_close_pdi_page' => 2, + 'pdf_closepath' => 2, + 'pdf_closepath_fill_stroke' => 2, + 'pdf_closepath_stroke' => 2, + 'pdf_concat' => 2, + 'pdf_continue_text' => 2, + 'pdf_create_gstate' => 2, + 'pdf_create_pvf' => 2, + 'pdf_curveto' => 2, + 'pdf_delete' => 2, + 'pdf_delete_pvf' => 2, + 'pdf_encoding_set_char' => 2, + 'pdf_end_font' => 2, + 'pdf_end_glyph' => 2, + 'pdf_end_page' => 2, + 'pdf_end_pattern' => 2, + 'pdf_end_template' => 2, + 'pdf_endpath' => 2, + 'pdf_execute_image' => 2, + 'pdf_fill' => 2, + 'pdf_fill_imageblock' => 2, + 'pdf_fill_pdfblock' => 2, + 'pdf_fill_stroke' => 2, + 'pdf_fill_textblock' => 2, + 'pdf_findfont' => 2, + 'pdf_fit_image' => 2, + 'pdf_fit_pdi_page' => 2, + 'pdf_fit_textline' => 2, + 'pdf_get_apiname' => 2, + 'pdf_get_buffer' => 2, + 'pdf_get_errmsg' => 2, + 'pdf_get_errnum' => 2, + 'pdf_get_info' => 2, + 'pdf_get_parameter' => 2, + 'pdf_get_pdi_parameter' => 2, + 'pdf_get_pdi_value' => 2, + 'pdf_get_value' => 2, + 'pdf_initgraphics' => 2, + 'pdf_lineto' => 2, + 'pdf_load_font' => 2, + 'pdf_load_iccprofile' => 2, + 'pdf_load_image' => 2, + 'pdf_makespotcolor' => 2, + 'pdf_moveto' => 2, + 'pdf_new' => 2, + 'pdf_open' => 2, + 'pdf_open_ccitt' => 2, + 'pdf_open_file' => 2, + 'pdf_open_gif' => 2, + 'pdf_open_image' => 2, + 'pdf_open_image_file' => 2, + 'pdf_open_jpeg' => 2, + 'pdf_open_memory_image' => 2, + 'pdf_open_pdi' => 2, + 'pdf_open_pdi_page' => 2, + 'pdf_place_image' => 2, + 'pdf_place_pdi_page' => 2, + 'pdf_process_pdi' => 2, + 'pdf_put_image' => 2, + 'pdf_rect' => 2, + 'pdf_restore' => 2, + 'pdf_rotate' => 2, + 'pdf_save' => 2, + 'pdf_scale' => 2, + 'pdf_set_border_color' => 2, + 'pdf_set_border_dash' => 2, + 'pdf_set_border_style' => 2, + 'pdf_set_char_spacing' => 2, + 'pdf_set_duration' => 2, + 'pdf_set_font' => 2, + 'pdf_set_gstate' => 2, + 'pdf_set_horiz_scaling' => 2, + 'pdf_set_info' => 2, + 'pdf_set_info_author' => 2, + 'pdf_set_info_creator' => 2, + 'pdf_set_info_keywords' => 2, + 'pdf_set_info_subject' => 2, + 'pdf_set_info_title' => 2, + 'pdf_set_leading' => 2, + 'pdf_set_parameter' => 2, + 'pdf_set_text_matrix' => 2, + 'pdf_set_text_pos' => 2, + 'pdf_set_text_rendering' => 2, + 'pdf_set_text_rise' => 2, + 'pdf_set_transition' => 2, + 'pdf_set_value' => 2, + 'pdf_set_word_spacing' => 2, + 'pdf_setcolor' => 2, + 'pdf_setdash' => 2, + 'pdf_setdashpattern' => 2, + 'pdf_setflat' => 2, + 'pdf_setfont' => 2, + 'pdf_setgray' => 2, + 'pdf_setgray_fill' => 2, + 'pdf_setgray_stroke' => 2, + 'pdf_setlinecap' => 2, + 'pdf_setlinejoin' => 2, + 'pdf_setlinewidth' => 2, + 'pdf_setmatrix' => 2, + 'pdf_setmiterlimit' => 2, + 'pdf_setpolydash' => 2, + 'pdf_setrgbcolor' => 2, + 'pdf_setrgbcolor_fill' => 2, + 'pdf_setrgbcolor_stroke' => 2, + 'pdf_shading' => 2, + 'pdf_shading_pattern' => 2, + 'pdf_shfill' => 2, + 'pdf_show' => 2, + 'pdf_show_boxed' => 2, + 'pdf_show_xy' => 2, + 'pdf_skew' => 2, + 'pdf_stringwidth' => 2, + 'pdf_stroke' => 2, + 'pdf_translate' => 2, + 'pdo_drivers' => 2, + 'pfsockopen' => 2, + 'pg_affected_rows' => 2, + 'pg_cancel_query' => 2, + 'pg_client_encoding' => 2, + 'pg_clientencoding' => 2, + 'pg_close' => 2, + 'pg_cmdtuples' => 2, + 'pg_connect' => 2, + 'pg_connection_busy' => 2, + 'pg_connection_reset' => 2, + 'pg_connection_status' => 2, + 'pg_convert' => 2, + 'pg_copy_from' => 2, + 'pg_copy_to' => 2, + 'pg_dbname' => 2, + 'pg_delete' => 2, + 'pg_end_copy' => 2, + 'pg_errormessage' => 2, + 'pg_escape_bytea' => 2, + 'pg_escape_string' => 2, + 'pg_exec' => 2, + 'pg_execute' => 2, + 'pg_fetch_all' => 2, + 'pg_fetch_all_columns' => 2, + 'pg_fetch_array' => 2, + 'pg_fetch_assoc' => 2, + 'pg_fetch_object' => 2, + 'pg_fetch_result' => 2, + 'pg_fetch_row' => 2, + 'pg_field_is_null' => 2, + 'pg_field_name' => 2, + 'pg_field_num' => 2, + 'pg_field_prtlen' => 2, + 'pg_field_size' => 2, + 'pg_field_table' => 2, + 'pg_field_type' => 2, + 'pg_field_type_oid' => 2, + 'pg_fieldisnull' => 2, + 'pg_fieldname' => 2, + 'pg_fieldnum' => 2, + 'pg_fieldprtlen' => 2, + 'pg_fieldsize' => 2, + 'pg_fieldtype' => 2, + 'pg_free_result' => 2, + 'pg_freeresult' => 2, + 'pg_get_notify' => 2, + 'pg_get_pid' => 2, + 'pg_get_result' => 2, + 'pg_getlastoid' => 2, + 'pg_host' => 2, + 'pg_insert' => 2, + 'pg_last_error' => 2, + 'pg_last_notice' => 2, + 'pg_last_oid' => 2, + 'pg_lo_close' => 2, + 'pg_lo_create' => 2, + 'pg_lo_export' => 2, + 'pg_lo_import' => 2, + 'pg_lo_open' => 2, + 'pg_lo_read' => 2, + 'pg_lo_read_all' => 2, + 'pg_lo_seek' => 2, + 'pg_lo_tell' => 2, + 'pg_lo_unlink' => 2, + 'pg_lo_write' => 2, + 'pg_loclose' => 2, + 'pg_locreate' => 2, + 'pg_loexport' => 2, + 'pg_loimport' => 2, + 'pg_loopen' => 2, + 'pg_loread' => 2, + 'pg_loreadall' => 2, + 'pg_lounlink' => 2, + 'pg_lowrite' => 2, + 'pg_meta_data' => 2, + 'pg_num_fields' => 2, + 'pg_num_rows' => 2, + 'pg_numfields' => 2, + 'pg_numrows' => 2, + 'pg_options' => 2, + 'pg_parameter_status' => 2, + 'pg_pconnect' => 2, + 'pg_ping' => 2, + 'pg_port' => 2, + 'pg_prepare' => 2, + 'pg_put_line' => 2, + 'pg_query' => 2, + 'pg_query_params' => 2, + 'pg_result' => 2, + 'pg_result_error' => 2, + 'pg_result_error_field' => 2, + 'pg_result_seek' => 2, + 'pg_result_status' => 2, + 'pg_select' => 2, + 'pg_send_execute' => 2, + 'pg_send_prepare' => 2, + 'pg_send_query' => 2, + 'pg_send_query_params' => 2, + 'pg_set_client_encoding' => 2, + 'pg_set_error_verbosity' => 2, + 'pg_setclientencoding' => 2, + 'pg_trace' => 2, + 'pg_transaction_status' => 2, + 'pg_tty' => 2, + 'pg_unescape_bytea' => 2, + 'pg_untrace' => 2, + 'pg_update' => 2, + 'pg_version' => 2, + 'php_egg_logo_guid' => 2, + 'php_ini_loaded_file' => 2, + 'php_ini_scanned_files' => 2, + 'php_logo_guid' => 2, + 'php_real_logo_guid' => 2, + 'php_sapi_name' => 2, + 'php_strip_whitespace' => 2, + 'php_uname' => 2, + 'phpcredits' => 2, + 'phpdoc_xml_from_string' => 2, + 'phpinfo' => 2, + 'phpversion' => 2, + 'pi' => 2, + 'png2wbmp' => 2, + 'pop3_close' => 2, + 'pop3_delete_message' => 2, + 'pop3_get_account_size' => 2, + 'pop3_get_message' => 2, + 'pop3_get_message_count' => 2, + 'pop3_get_message_header' => 2, + 'pop3_get_message_ids' => 2, + 'pop3_get_message_size' => 2, + 'pop3_get_message_sizes' => 2, + 'pop3_open' => 2, + 'pop3_undelete' => 2, + 'popen' => 2, + 'pos' => 2, + 'posix_ctermid' => 2, + 'posix_errno' => 2, + 'posix_get_last_error' => 2, + 'posix_getcwd' => 2, + 'posix_getegid' => 2, + 'posix_geteuid' => 2, + 'posix_getgid' => 2, + 'posix_getgrgid' => 2, + 'posix_getgrnam' => 2, + 'posix_getgroups' => 2, + 'posix_getlogin' => 2, + 'posix_getpgid' => 2, + 'posix_getpgrp' => 2, + 'posix_getpid' => 2, + 'posix_getppid' => 2, + 'posix_getpwnam' => 2, + 'posix_getpwuid' => 2, + 'posix_getrlimit' => 2, + 'posix_getsid' => 2, + 'posix_getuid' => 2, + 'posix_isatty' => 2, + 'posix_kill' => 2, + 'posix_mkfifo' => 2, + 'posix_setegid' => 2, + 'posix_seteuid' => 2, + 'posix_setgid' => 2, + 'posix_setpgid' => 2, + 'posix_setsid' => 2, + 'posix_setuid' => 2, + 'posix_strerror' => 2, + 'posix_times' => 2, + 'posix_ttyname' => 2, + 'posix_uname' => 2, + 'pow' => 2, + 'preg_filter' => 2, + 'preg_grep' => 2, + 'preg_last_error' => 2, + 'preg_match' => 2, + 'preg_match_all' => 2, + 'preg_quote' => 2, + 'preg_replace' => 2, + 'preg_replace_callback' => 2, + 'preg_split' => 2, + 'prev' => 2, + 'print_r' => 2, + 'printf' => 2, + 'proc_close' => 2, + 'proc_get_status' => 2, + 'proc_open' => 2, + 'proc_terminate' => 2, + 'property_exists' => 2, + 'putenv' => 2, + 'quoted_printable_decode' => 2, + 'quoted_printable_encode' => 2, + 'quotemeta' => 2, + 'rad2deg' => 2, + 'radius_acct_open' => 2, + 'radius_add_server' => 2, + 'radius_auth_open' => 2, + 'radius_close' => 2, + 'radius_config' => 2, + 'radius_create_request' => 2, + 'radius_cvt_addr' => 2, + 'radius_cvt_int' => 2, + 'radius_cvt_string' => 2, + 'radius_demangle' => 2, + 'radius_demangle_mppe_key' => 2, + 'radius_get_attr' => 2, + 'radius_get_vendor_attr' => 2, + 'radius_put_addr' => 2, + 'radius_put_attr' => 2, + 'radius_put_int' => 2, + 'radius_put_string' => 2, + 'radius_put_vendor_addr' => 2, + 'radius_put_vendor_attr' => 2, + 'radius_put_vendor_int' => 2, + 'radius_put_vendor_string' => 2, + 'radius_request_authenticator' => 2, + 'radius_send_request' => 2, + 'radius_server_secret' => 2, + 'radius_strerror' => 2, + 'rand' => 2, + 'range' => 2, + 'rawurldecode' => 2, + 'rawurlencode' => 2, + 'read_exif_data' => 2, + 'readdir' => 2, + 'readfile' => 2, + 'readgzfile' => 2, + 'readlink' => 2, + 'realpath' => 2, + 'realpath_cache_get' => 2, + 'realpath_cache_size' => 2, + 'recode_file' => 2, + 'recode_string' => 2, + 'reg_close_key' => 2, + 'reg_create_key' => 2, + 'reg_enum_key' => 2, + 'reg_enum_value' => 2, + 'reg_get_value' => 2, + 'reg_open_key' => 2, + 'reg_set_value' => 2, + 'register_shutdown_function' => 2, + 'register_tick_function' => 2, + 'rename' => 2, + 'res_close' => 2, + 'res_get' => 2, + 'res_list' => 2, + 'res_list_type' => 2, + 'res_open' => 2, + 'res_set' => 2, + 'reset' => 2, + 'resourcebundle_count' => 2, + 'resourcebundle_create' => 2, + 'resourcebundle_get' => 2, + 'resourcebundle_get_error_code' => 2, + 'resourcebundle_get_error_message' => 2, + 'resourcebundle_locales' => 2, + 'restore_error_handler' => 2, + 'restore_exception_handler' => 2, + 'restore_include_path' => 2, + 'rewind' => 2, + 'rewinddir' => 2, + 'rmdir' => 2, + 'round' => 2, + 'rsort' => 2, + 'rtrim' => 2, + 'runkit_class_adopt' => 2, + 'runkit_class_emancipate' => 2, + 'runkit_constant_add' => 2, + 'runkit_constant_redefine' => 2, + 'runkit_constant_remove' => 2, + 'runkit_default_property_add' => 2, + 'runkit_function_add' => 2, + 'runkit_function_copy' => 2, + 'runkit_function_redefine' => 2, + 'runkit_function_remove' => 2, + 'runkit_function_rename' => 2, + 'runkit_import' => 2, + 'runkit_lint' => 2, + 'runkit_lint_file' => 2, + 'runkit_method_add' => 2, + 'runkit_method_copy' => 2, + 'runkit_method_redefine' => 2, + 'runkit_method_remove' => 2, + 'runkit_method_rename' => 2, + 'runkit_object_id' => 2, + 'runkit_return_value_used' => 2, + 'runkit_sandbox_output_handler' => 2, + 'runkit_superglobals' => 2, + 'runkit_zval_inspect' => 2, + 'scandir' => 2, + 'sem_acquire' => 2, + 'sem_get' => 2, + 'sem_release' => 2, + 'sem_remove' => 2, + 'serialize' => 2, + 'session_cache_expire' => 2, + 'session_cache_limiter' => 2, + 'session_commit' => 2, + 'session_decode' => 2, + 'session_destroy' => 2, + 'session_encode' => 2, + 'session_get_cookie_params' => 2, + 'session_id' => 2, + 'session_is_registered' => 2, + 'session_module_name' => 2, + 'session_name' => 2, + 'session_regenerate_id' => 2, + 'session_register' => 2, + 'session_save_path' => 2, + 'session_set_cookie_params' => 2, + 'session_set_save_handler' => 2, + 'session_start' => 2, + 'session_unregister' => 2, + 'session_unset' => 2, + 'session_write_close' => 2, + 'set_content' => 2, + 'set_error_handler' => 2, + 'set_exception_handler' => 2, + 'set_file_buffer' => 2, + 'set_include_path' => 2, + 'set_magic_quotes_runtime' => 2, + 'set_socket_blocking' => 2, + 'set_time_limit' => 2, + 'setcookie' => 2, + 'setlocale' => 2, + 'setrawcookie' => 2, + 'settype' => 2, + 'sha1' => 2, + 'sha1_file' => 2, + 'shell_exec' => 2, + 'shm_attach' => 2, + 'shm_detach' => 2, + 'shm_get_var' => 2, + 'shm_put_var' => 2, + 'shm_remove' => 2, + 'shm_remove_var' => 2, + 'shmop_close' => 2, + 'shmop_delete' => 2, + 'shmop_open' => 2, + 'shmop_read' => 2, + 'shmop_size' => 2, + 'shmop_write' => 2, + 'show_source' => 2, + 'shuffle' => 2, + 'similar_text' => 2, + 'simplexml_import_dom' => 2, + 'simplexml_load_file' => 2, + 'simplexml_load_string' => 2, + 'sin' => 2, + 'sinh' => 2, + 'sizeof' => 2, + 'sleep' => 2, + 'smtp_close' => 2, + 'smtp_cmd_data' => 2, + 'smtp_cmd_mail' => 2, + 'smtp_cmd_rcpt' => 2, + 'smtp_connect' => 2, + 'snmp2_get' => 2, + 'snmp2_getnext' => 2, + 'snmp2_real_walk' => 2, + 'snmp2_set' => 2, + 'snmp2_walk' => 2, + 'snmp3_get' => 2, + 'snmp3_getnext' => 2, + 'snmp3_real_walk' => 2, + 'snmp3_set' => 2, + 'snmp3_walk' => 2, + 'snmp_get_quick_print' => 2, + 'snmp_get_valueretrieval' => 2, + 'snmp_read_mib' => 2, + 'snmp_set_quick_print' => 2, + 'snmp_set_valueretrieval' => 2, + 'snmpget' => 2, + 'snmpgetnext' => 2, + 'snmprealwalk' => 2, + 'snmpset' => 2, + 'snmpwalk' => 2, + 'snmpwalkoid' => 2, + 'socket_accept' => 2, + 'socket_bind' => 2, + 'socket_clear_error' => 2, + 'socket_close' => 2, + 'socket_connect' => 2, + 'socket_create' => 2, + 'socket_create_listen' => 2, + 'socket_create_pair' => 2, + 'socket_get_option' => 2, + 'socket_get_status' => 2, + 'socket_getopt' => 2, + 'socket_getpeername' => 2, + 'socket_getsockname' => 2, + 'socket_iovec_add' => 2, + 'socket_iovec_alloc' => 2, + 'socket_iovec_delete' => 2, + 'socket_iovec_fetch' => 2, + 'socket_iovec_free' => 2, + 'socket_iovec_set' => 2, + 'socket_last_error' => 2, + 'socket_listen' => 2, + 'socket_read' => 2, + 'socket_readv' => 2, + 'socket_recv' => 2, + 'socket_recvfrom' => 2, + 'socket_recvmsg' => 2, + 'socket_select' => 2, + 'socket_send' => 2, + 'socket_sendmsg' => 2, + 'socket_sendto' => 2, + 'socket_set_block' => 2, + 'socket_set_blocking' => 2, + 'socket_set_nonblock' => 2, + 'socket_set_option' => 2, + 'socket_set_timeout' => 2, + 'socket_setopt' => 2, + 'socket_shutdown' => 2, + 'socket_strerror' => 2, + 'socket_write' => 2, + 'socket_writev' => 2, + 'solid_close' => 2, + 'solid_connect' => 2, + 'solid_exec' => 2, + 'solid_fetchrow' => 2, + 'solid_fieldname' => 2, + 'solid_fieldnum' => 2, + 'solid_freeresult' => 2, + 'solid_numfields' => 2, + 'solid_numrows' => 2, + 'solid_result' => 2, + 'sort' => 2, + 'soundex' => 2, + 'spl_autoload' => 2, + 'spl_autoload_call' => 2, + 'spl_autoload_extensions' => 2, + 'spl_autoload_functions' => 2, + 'spl_autoload_register' => 2, + 'spl_autoload_unregister' => 2, + 'spl_classes' => 2, + 'spl_object_hash' => 2, + 'split' => 2, + 'spliti' => 2, + 'sprintf' => 2, + 'sql_regcase' => 2, + 'sqlite_array_query' => 2, + 'sqlite_busy_timeout' => 2, + 'sqlite_changes' => 2, + 'sqlite_close' => 2, + 'sqlite_column' => 2, + 'sqlite_create_aggregate' => 2, + 'sqlite_create_function' => 2, + 'sqlite_current' => 2, + 'sqlite_error_string' => 2, + 'sqlite_escape_string' => 2, + 'sqlite_exec' => 2, + 'sqlite_factory' => 2, + 'sqlite_fetch_all' => 2, + 'sqlite_fetch_array' => 2, + 'sqlite_fetch_column_types' => 2, + 'sqlite_fetch_object' => 2, + 'sqlite_fetch_single' => 2, + 'sqlite_fetch_string' => 2, + 'sqlite_field_name' => 2, + 'sqlite_has_more' => 2, + 'sqlite_has_prev' => 2, + 'sqlite_last_error' => 2, + 'sqlite_last_insert_rowid' => 2, + 'sqlite_libencoding' => 2, + 'sqlite_libversion' => 2, + 'sqlite_next' => 2, + 'sqlite_num_fields' => 2, + 'sqlite_num_rows' => 2, + 'sqlite_open' => 2, + 'sqlite_popen' => 2, + 'sqlite_prev' => 2, + 'sqlite_query' => 2, + 'sqlite_rewind' => 2, + 'sqlite_seek' => 2, + 'sqlite_single_query' => 2, + 'sqlite_udf_decode_binary' => 2, + 'sqlite_udf_encode_binary' => 2, + 'sqlite_unbuffered_query' => 2, + 'sqlite_valid' => 2, + 'sqrt' => 2, + 'srand' => 2, + 'sscanf' => 2, + 'ssh2_auth_hostbased_file' => 2, + 'ssh2_auth_none' => 2, + 'ssh2_auth_password' => 2, + 'ssh2_auth_pubkey_file' => 2, + 'ssh2_connect' => 2, + 'ssh2_exec' => 2, + 'ssh2_fetch_stream' => 2, + 'ssh2_fingerprint' => 2, + 'ssh2_forward_accept' => 2, + 'ssh2_forward_listen' => 2, + 'ssh2_methods_negotiated' => 2, + 'ssh2_poll' => 2, + 'ssh2_publickey_add' => 2, + 'ssh2_publickey_init' => 2, + 'ssh2_publickey_list' => 2, + 'ssh2_publickey_remove' => 2, + 'ssh2_scp_recv' => 2, + 'ssh2_scp_send' => 2, + 'ssh2_sftp' => 2, + 'ssh2_sftp_lstat' => 2, + 'ssh2_sftp_mkdir' => 2, + 'ssh2_sftp_readlink' => 2, + 'ssh2_sftp_realpath' => 2, + 'ssh2_sftp_rename' => 2, + 'ssh2_sftp_rmdir' => 2, + 'ssh2_sftp_stat' => 2, + 'ssh2_sftp_symlink' => 2, + 'ssh2_sftp_unlink' => 2, + 'ssh2_shell' => 2, + 'ssh2_tunnel' => 2, + 'stat' => 2, + 'stats_absolute_deviation' => 2, + 'stats_cdf_beta' => 2, + 'stats_cdf_binomial' => 2, + 'stats_cdf_cauchy' => 2, + 'stats_cdf_chisquare' => 2, + 'stats_cdf_exponential' => 2, + 'stats_cdf_f' => 2, + 'stats_cdf_gamma' => 2, + 'stats_cdf_laplace' => 2, + 'stats_cdf_logistic' => 2, + 'stats_cdf_negative_binomial' => 2, + 'stats_cdf_noncentral_chisquare' => 2, + 'stats_cdf_noncentral_f' => 2, + 'stats_cdf_noncentral_t' => 2, + 'stats_cdf_normal' => 2, + 'stats_cdf_poisson' => 2, + 'stats_cdf_t' => 2, + 'stats_cdf_uniform' => 2, + 'stats_cdf_weibull' => 2, + 'stats_covariance' => 2, + 'stats_dens_beta' => 2, + 'stats_dens_cauchy' => 2, + 'stats_dens_chisquare' => 2, + 'stats_dens_exponential' => 2, + 'stats_dens_f' => 2, + 'stats_dens_gamma' => 2, + 'stats_dens_laplace' => 2, + 'stats_dens_logistic' => 2, + 'stats_dens_normal' => 2, + 'stats_dens_pmf_binomial' => 2, + 'stats_dens_pmf_hypergeometric' => 2, + 'stats_dens_pmf_negative_binomial' => 2, + 'stats_dens_pmf_poisson' => 2, + 'stats_dens_t' => 2, + 'stats_dens_uniform' => 2, + 'stats_dens_weibull' => 2, + 'stats_harmonic_mean' => 2, + 'stats_kurtosis' => 2, + 'stats_rand_gen_beta' => 2, + 'stats_rand_gen_chisquare' => 2, + 'stats_rand_gen_exponential' => 2, + 'stats_rand_gen_f' => 2, + 'stats_rand_gen_funiform' => 2, + 'stats_rand_gen_gamma' => 2, + 'stats_rand_gen_ipoisson' => 2, + 'stats_rand_gen_iuniform' => 2, + 'stats_rand_gen_noncenral_f' => 2, + 'stats_rand_gen_noncentral_chisquare' => 2, + 'stats_rand_gen_noncentral_t' => 2, + 'stats_rand_gen_normal' => 2, + 'stats_rand_gen_t' => 2, + 'stats_rand_getsd' => 2, + 'stats_rand_ibinomial' => 2, + 'stats_rand_ibinomial_negative' => 2, + 'stats_rand_ignlgi' => 2, + 'stats_rand_phrase_to_seeds' => 2, + 'stats_rand_ranf' => 2, + 'stats_rand_setall' => 2, + 'stats_skew' => 2, + 'stats_standard_deviation' => 2, + 'stats_stat_binomial_coef' => 2, + 'stats_stat_correlation' => 2, + 'stats_stat_factorial' => 2, + 'stats_stat_independent_t' => 2, + 'stats_stat_innerproduct' => 2, + 'stats_stat_paired_t' => 2, + 'stats_stat_percentile' => 2, + 'stats_stat_powersum' => 2, + 'stats_variance' => 2, + 'str_getcsv' => 2, + 'str_ireplace' => 2, + 'str_pad' => 2, + 'str_repeat' => 2, + 'str_replace' => 2, + 'str_rot13' => 2, + 'str_shuffle' => 2, + 'str_split' => 2, + 'str_word_count' => 2, + 'strcasecmp' => 2, + 'strchr' => 2, + 'strcmp' => 2, + 'strcoll' => 2, + 'strcspn' => 2, + 'stream_bucket_append' => 2, + 'stream_bucket_make_writeable' => 2, + 'stream_bucket_new' => 2, + 'stream_bucket_prepend' => 2, + 'stream_context_create' => 2, + 'stream_context_get_default' => 2, + 'stream_context_get_options' => 2, + 'stream_context_get_params' => 2, + 'stream_context_set_default' => 2, + 'stream_context_set_option' => 2, + 'stream_context_set_params' => 2, + 'stream_copy_to_stream' => 2, + 'stream_encoding' => 2, + 'stream_filter_append' => 2, + 'stream_filter_prepend' => 2, + 'stream_filter_register' => 2, + 'stream_filter_remove' => 2, + 'stream_get_contents' => 2, + 'stream_get_filters' => 2, + 'stream_get_line' => 2, + 'stream_get_meta_data' => 2, + 'stream_get_transports' => 2, + 'stream_get_wrappers' => 2, + 'stream_is_local' => 2, + 'stream_notification_callback' => 2, + 'stream_register_wrapper' => 2, + 'stream_resolve_include_path' => 2, + 'stream_select' => 2, + 'stream_set_blocking' => 2, + 'stream_set_chunk_size' => 2, + 'stream_set_read_buffer' => 2, + 'stream_set_timeout' => 2, + 'stream_set_write_buffer' => 2, + 'stream_socket_accept' => 2, + 'stream_socket_client' => 2, + 'stream_socket_enable_crypto' => 2, + 'stream_socket_get_name' => 2, + 'stream_socket_pair' => 2, + 'stream_socket_recvfrom' => 2, + 'stream_socket_sendto' => 2, + 'stream_socket_server' => 2, + 'stream_socket_shutdown' => 2, + 'stream_supports_lock' => 2, + 'stream_wrapper_register' => 2, + 'stream_wrapper_restore' => 2, + 'stream_wrapper_unregister' => 2, + 'strftime' => 2, + 'strip_tags' => 2, + 'stripcslashes' => 2, + 'stripos' => 2, + 'stripslashes' => 2, + 'stristr' => 2, + 'strlen' => 2, + 'strnatcasecmp' => 2, + 'strnatcmp' => 2, + 'strncasecmp' => 2, + 'strncmp' => 2, + 'strpbrk' => 2, + 'strpos' => 2, + 'strrchr' => 2, + 'strrev' => 2, + 'strripos' => 2, + 'strrpos' => 2, + 'strspn' => 2, + 'strstr' => 2, + 'strtok' => 2, + 'strtolower' => 2, + 'strtotime' => 2, + 'strtoupper' => 2, + 'strtr' => 2, + 'strval' => 2, + 'substr' => 2, + 'substr_compare' => 2, + 'substr_count' => 2, + 'substr_replac' => 2, + 'substr_replace' => 2, + 'svn_add' => 2, + 'svn_auth_get_parameter' => 2, + 'svn_auth_set_parameter' => 2, + 'svn_cat' => 2, + 'svn_checkout' => 2, + 'svn_cleanup' => 2, + 'svn_client_version' => 2, + 'svn_commit' => 2, + 'svn_diff' => 2, + 'svn_export' => 2, + 'svn_fs_abort_txn' => 2, + 'svn_fs_apply_text' => 2, + 'svn_fs_begin_txn2' => 2, + 'svn_fs_change_node_prop' => 2, + 'svn_fs_check_path' => 2, + 'svn_fs_contents_changed' => 2, + 'svn_fs_copy' => 2, + 'svn_fs_delete' => 2, + 'svn_fs_dir_entries' => 2, + 'svn_fs_file_contents' => 2, + 'svn_fs_file_length' => 2, + 'svn_fs_is_dir' => 2, + 'svn_fs_is_file' => 2, + 'svn_fs_make_dir' => 2, + 'svn_fs_make_file' => 2, + 'svn_fs_node_created_rev' => 2, + 'svn_fs_node_prop' => 2, + 'svn_fs_props_changed' => 2, + 'svn_fs_revision_prop' => 2, + 'svn_fs_revision_root' => 2, + 'svn_fs_txn_root' => 2, + 'svn_fs_youngest_rev' => 2, + 'svn_import' => 2, + 'svn_info' => 2, + 'svn_log' => 2, + 'svn_ls' => 2, + 'svn_repos_create' => 2, + 'svn_repos_fs' => 2, + 'svn_repos_fs_begin_txn_for_commit' => 2, + 'svn_repos_fs_commit_txn' => 2, + 'svn_repos_hotcopy' => 2, + 'svn_repos_open' => 2, + 'svn_repos_recover' => 2, + 'svn_status' => 2, + 'svn_update' => 2, + 'sybase_affected_rows' => 2, + 'sybase_close' => 2, + 'sybase_connect' => 2, + 'sybase_data_seek' => 2, + 'sybase_fetch_array' => 2, + 'sybase_fetch_field' => 2, + 'sybase_fetch_object' => 2, + 'sybase_fetch_row' => 2, + 'sybase_field_seek' => 2, + 'sybase_free_result' => 2, + 'sybase_num_fields' => 2, + 'sybase_num_rows' => 2, + 'sybase_pconnect' => 2, + 'sybase_query' => 2, + 'sybase_result' => 2, + 'sybase_select_db' => 2, + 'symlink' => 2, + 'syntax' => 2, + 'sys_get_temp_dir' => 2, + 'syslog' => 2, + 'system' => 2, + 'tan' => 2, + 'tanh' => 2, + 'tempnam' => 2, + 'textdomain' => 2, + 'thread_get' => 2, + 'thread_include' => 2, + 'thread_lock' => 2, + 'thread_lock_try' => 2, + 'thread_mutex_destroy' => 2, + 'thread_mutex_init' => 2, + 'thread_set' => 2, + 'thread_start' => 2, + 'thread_unlock' => 2, + 'tidy_access_count' => 2, + 'tidy_clean_repair' => 2, + 'tidy_config_count' => 2, + 'tidy_diagnose' => 2, + 'tidy_error_count' => 2, + 'tidy_get_body' => 2, + 'tidy_get_config' => 2, + 'tidy_get_error_buffer' => 2, + 'tidy_get_head' => 2, + 'tidy_get_html' => 2, + 'tidy_get_html_ver' => 2, + 'tidy_get_output' => 2, + 'tidy_get_release' => 2, + 'tidy_get_root' => 2, + 'tidy_get_status' => 2, + 'tidy_getopt' => 2, + 'tidy_is_xhtml' => 2, + 'tidy_is_xml' => 2, + 'tidy_parse_file' => 2, + 'tidy_parse_string' => 2, + 'tidy_repair_file' => 2, + 'tidy_repair_string' => 2, + 'tidy_warning_count' => 2, + 'time' => 2, + 'time_nanosleep' => 2, + 'time_sleep_until' => 2, + 'timezone_abbreviations_list' => 2, + 'timezone_identifiers_list' => 2, + 'timezone_location_get' => 2, + 'timezone_name_from_abbr' => 2, + 'timezone_name_get' => 2, + 'timezone_offset_get' => 2, + 'timezone_open' => 2, + 'timezone_transitions_get' => 2, + 'timezone_version_get' => 2, + 'tmpfile' => 2, + 'token_get_all' => 2, + 'token_name' => 2, + 'touch' => 2, + 'trait_exists' => 2, + 'transliterate' => 2, + 'transliterate_filters_get' => 2, + 'trigger_error' => 2, + 'trim' => 2, + 'uasort' => 2, + 'ucfirst' => 2, + 'ucwords' => 2, + 'uksort' => 2, + 'umask' => 2, + 'uniqid' => 2, + 'unixtojd' => 2, + 'unlink' => 2, + 'unpack' => 2, + 'unregister_tick_function' => 2, + 'unserialize' => 2, + 'urldecode' => 2, + 'urlencode' => 2, + 'use_soap_error_handler' => 2, + 'user_error' => 2, + 'usleep' => 2, + 'usort' => 2, + 'utf8_decode' => 2, + 'utf8_encode' => 2, + 'var_dump' => 2, + 'var_export' => 2, + 'variant_abs' => 2, + 'variant_add' => 2, + 'variant_and' => 2, + 'variant_cast' => 2, + 'variant_cat' => 2, + 'variant_cmp' => 2, + 'variant_date_from_timestamp' => 2, + 'variant_date_to_timestamp' => 2, + 'variant_div' => 2, + 'variant_eqv' => 2, + 'variant_fix' => 2, + 'variant_get_type' => 2, + 'variant_idiv' => 2, + 'variant_imp' => 2, + 'variant_int' => 2, + 'variant_mod' => 2, + 'variant_mul' => 2, + 'variant_neg' => 2, + 'variant_not' => 2, + 'variant_or' => 2, + 'variant_pow' => 2, + 'variant_round' => 2, + 'variant_set' => 2, + 'variant_set_type' => 2, + 'variant_sub' => 2, + 'variant_xor' => 2, + 'version_compare' => 2, + 'vfprintf' => 2, + 'virtual' => 2, + 'vm_addalias' => 2, + 'vm_adduser' => 2, + 'vm_delalias' => 2, + 'vm_deluser' => 2, + 'vm_passwd' => 2, + 'vprintf' => 2, + 'vsprintf' => 2, + 'wddx_add_vars' => 2, + 'wddx_deserialize' => 2, + 'wddx_packet_end' => 2, + 'wddx_packet_start' => 2, + 'wddx_serialize_value' => 2, + 'wddx_serialize_vars' => 2, + 'win32_create_service' => 2, + 'win32_delete_service' => 2, + 'win32_get_last_control_message' => 2, + 'win32_ps_list_procs' => 2, + 'win32_ps_stat_mem' => 2, + 'win32_ps_stat_proc' => 2, + 'win32_query_service_status' => 2, + 'win32_scheduler_delete_task' => 2, + 'win32_scheduler_enum_tasks' => 2, + 'win32_scheduler_get_task_info' => 2, + 'win32_scheduler_run' => 2, + 'win32_scheduler_set_task_info' => 2, + 'win32_set_service_status' => 2, + 'win32_start_service' => 2, + 'win32_start_service_ctrl_dispatcher' => 2, + 'win32_stop_service' => 2, + 'win_beep' => 2, + 'win_browse_file' => 2, + 'win_browse_folder' => 2, + 'win_create_link' => 2, + 'win_message_box' => 2, + 'win_play_wav' => 2, + 'win_shell_execute' => 2, + 'wordwrap' => 2, + 'xml_error_string' => 2, + 'xml_get_current_byte_index' => 2, + 'xml_get_current_column_number' => 2, + 'xml_get_current_line_number' => 2, + 'xml_get_error_code' => 2, + 'xml_parse' => 2, + 'xml_parse_into_struct' => 2, + 'xml_parser_create' => 2, + 'xml_parser_create_ns' => 2, + 'xml_parser_free' => 2, + 'xml_parser_get_option' => 2, + 'xml_parser_set_option' => 2, + 'xml_set_character_data_handler' => 2, + 'xml_set_default_handler' => 2, + 'xml_set_element_handler' => 2, + 'xml_set_end_namespace_decl_handler' => 2, + 'xml_set_external_entity_ref_handler' => 2, + 'xml_set_notation_decl_handler' => 2, + 'xml_set_object' => 2, + 'xml_set_processing_instruction_handler' => 2, + 'xml_set_start_namespace_decl_handler' => 2, + 'xml_set_unparsed_entity_decl_handler' => 2, + 'xmldoc' => 2, + 'xmldocfile' => 2, + 'xmlrpc_decode' => 2, + 'xmlrpc_decode_request' => 2, + 'xmlrpc_encode' => 2, + 'xmlrpc_encode_request' => 2, + 'xmlrpc_get_type' => 2, + 'xmlrpc_is_fault' => 2, + 'xmlrpc_parse_method_descriptions' => 2, + 'xmlrpc_server_add_introspection_data' => 2, + 'xmlrpc_server_call_method' => 2, + 'xmlrpc_server_create' => 2, + 'xmlrpc_server_destroy' => 2, + 'xmlrpc_server_register_introspection_callback' => 2, + 'xmlrpc_server_register_method' => 2, + 'xmlrpc_set_type' => 2, + 'xmltree' => 2, + 'xmlwriter_end_attribute' => 2, + 'xmlwriter_end_cdata' => 2, + 'xmlwriter_end_comment' => 2, + 'xmlwriter_end_document' => 2, + 'xmlwriter_end_dtd' => 2, + 'xmlwriter_end_dtd_attlist' => 2, + 'xmlwriter_end_dtd_element' => 2, + 'xmlwriter_end_dtd_entity' => 2, + 'xmlwriter_end_element' => 2, + 'xmlwriter_end_pi' => 2, + 'xmlwriter_flush' => 2, + 'xmlwriter_full_end_element' => 2, + 'xmlwriter_open_memory' => 2, + 'xmlwriter_open_uri' => 2, + 'xmlwriter_output_memory' => 2, + 'xmlwriter_set_indent' => 2, + 'xmlwriter_set_indent_string' => 2, + 'xmlwriter_start_attribute' => 2, + 'xmlwriter_start_attribute_ns' => 2, + 'xmlwriter_start_cdata' => 2, + 'xmlwriter_start_comment' => 2, + 'xmlwriter_start_document' => 2, + 'xmlwriter_start_dtd' => 2, + 'xmlwriter_start_dtd_attlist' => 2, + 'xmlwriter_start_dtd_element' => 2, + 'xmlwriter_start_dtd_entity' => 2, + 'xmlwriter_start_element' => 2, + 'xmlwriter_start_element_ns' => 2, + 'xmlwriter_start_pi' => 2, + 'xmlwriter_text' => 2, + 'xmlwriter_write_attribute' => 2, + 'xmlwriter_write_attribute_ns' => 2, + 'xmlwriter_write_cdata' => 2, + 'xmlwriter_write_comment' => 2, + 'xmlwriter_write_dtd' => 2, + 'xmlwriter_write_dtd_attlist' => 2, + 'xmlwriter_write_dtd_element' => 2, + 'xmlwriter_write_dtd_entity' => 2, + 'xmlwriter_write_element' => 2, + 'xmlwriter_write_element_ns' => 2, + 'xmlwriter_write_pi' => 2, + 'xmlwriter_write_raw' => 2, + 'xpath_eval' => 2, + 'xpath_eval_expression' => 2, + 'xpath_new_context' => 2, + 'xpath_register_ns' => 2, + 'xpath_register_ns_auto' => 2, + 'xptr_eval' => 2, + 'xptr_new_context' => 2, + 'yp_all' => 2, + 'yp_cat' => 2, + 'yp_err_string' => 2, + 'yp_errno' => 2, + 'yp_first' => 2, + 'yp_get_default_domain' => 2, + 'yp_master' => 2, + 'yp_match' => 2, + 'yp_next' => 2, + 'yp_order' => 2, + 'zend_current_obfuscation_level' => 2, + 'zend_get_cfg_var' => 2, + 'zend_get_id' => 2, + 'zend_loader_current_file' => 2, + 'zend_loader_enabled' => 2, + 'zend_loader_file_encoded' => 2, + 'zend_loader_file_licensed' => 2, + 'zend_loader_install_license' => 2, + 'zend_loader_version' => 2, + 'zend_logo_guid' => 2, + 'zend_match_hostmasks' => 2, + 'zend_obfuscate_class_name' => 2, + 'zend_obfuscate_function_name' => 2, + 'zend_optimizer_version' => 2, + 'zend_runtime_obfuscate' => 2, + 'zend_version' => 2, + 'zip_close' => 2, + 'zip_entry_close' => 2, + 'zip_entry_compressedsize' => 2, + 'zip_entry_compressionmethod' => 2, + 'zip_entry_filesize' => 2, + 'zip_entry_name' => 2, + 'zip_entry_open' => 2, + 'zip_entry_read' => 2, + 'zip_open' => 2, + 'zip_read' => 2, + 'zlib_decode' => 2, + 'zlib_encode' => 2, + 'zlib_get_coding_type' => 2 + ), + Generator::CASE_INSENSITIVE + ); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Python.php b/apigen/libs/FSHL/FSHL/Lexer/Python.php new file mode 100644 index 00000000000..2945f5f2703 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Python.php @@ -0,0 +1,502 @@ + array( + array( + 'ALPHA' => array('KEYWORD', Generator::BACK), + '_' => array('KEYWORD', Generator::BACK), + '\'\'\'' => array('DOCSTRING1', Generator::NEXT), + '"""' => array('DOCSTRING2', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '#' => array('COMMENT_LINE', Generator::NEXT), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'KEYWORD' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'DOCSTRING1' => array( + array( + '\'\'\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\\'\'\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'py-docstring', + null + ), + 'DOCSTRING2' => array( + array( + '"""' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\"""' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'py-docstring', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'py-quote', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\\' => array(Generator::STATE_SELF, Generator::NEXT), + '\\"' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'py-quote', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'py-comment', + null + ), + 'NUMBER' => array( + array( + 'DOTNUM' => array('FRACTION', Generator::NEXT), + 'l' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'L' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'j' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'J' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'e-' => array('EXPONENT', Generator::NEXT), + 'e+' => array('EXPONENT', Generator::NEXT), + 'e' => array('EXPONENT', Generator::NEXT), + 'x' => array('HEXA', Generator::NEXT), + 'X' => array('HEXA', Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'py-num', + null + ), + 'FRACTION' => array( + array( + 'j' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'J' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'e-' => array('EXPONENT', Generator::NEXT), + 'e+' => array('EXPONENT', Generator::NEXT), + 'e' => array('EXPONENT', Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'py-num', + null + ), + 'EXPONENT' => array( + array( + 'j' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'J' => array(Generator::STATE_RETURN, Generator::CURRENT), + '!NUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'py-num', + null + ), + 'HEXA' => array( + array( + 'L' => array(Generator::STATE_RETURN, Generator::CURRENT), + 'l' => array(Generator::STATE_RETURN, Generator::CURRENT), + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'py-num', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'py-keyword', + array( + 'and' => 1, + 'as' => 1, + 'assert' => 1, + 'break' => 1, + 'class' => 1, + 'continue' => 1, + 'def' => 1, + 'del' => 1, + 'elif' => 1, + 'else' => 1, + 'except' => 1, + 'exec' => 1, + 'finally' => 1, + 'for' => 1, + 'from' => 1, + 'global' => 1, + 'if' => 1, + 'import' => 1, + 'in' => 1, + 'is' => 1, + 'lambda' => 1, + 'not' => 1, + 'or' => 1, + 'pass' => 1, + 'print' => 1, + 'raise' => 1, + 'return' => 1, + 'try' => 1, + 'while' => 1, + 'with' => 1, + 'yield' => 1, + + 'abs' => 2, + 'all' => 2, + 'any' => 2, + 'apply' => 2, + 'basestring' => 2, + 'bool' => 2, + 'buffer' => 2, + 'callable' => 2, + 'chr' => 2, + 'classmethod' => 2, + 'cmp' => 2, + 'coerce' => 2, + 'compile' => 2, + 'complex' => 2, + 'delattr' => 2, + 'dict' => 2, + 'dir' => 2, + 'divmod' => 2, + 'enumerate' => 2, + 'eval' => 2, + 'execfile' => 2, + 'file' => 2, + 'filter' => 2, + 'float' => 2, + 'frozenset' => 2, + 'getattr' => 2, + 'globals' => 2, + 'hasattr' => 2, + 'hash' => 2, + 'hex' => 2, + 'id' => 2, + 'input' => 2, + 'int' => 2, + 'intern' => 2, + 'isinstance' => 2, + 'issubclass' => 2, + 'iter' => 2, + 'len' => 2, + 'list' => 2, + 'locals' => 2, + 'long' => 2, + 'map' => 2, + 'max' => 2, + 'min' => 2, + 'object' => 2, + 'oct' => 2, + 'open' => 2, + 'ord' => 2, + 'pow' => 2, + 'property' => 2, + 'range' => 2, + 'raw_input' => 2, + 'reduce' => 2, + 'reload' => 2, + 'repr' => 2, + 'reversed' => 2, + 'round' => 2, + 'set' => 2, + 'setattr' => 2, + 'slice' => 2, + 'sorted' => 2, + 'staticmethod' => 2, + 'str' => 2, + 'sum' => 2, + 'super' => 2, + 'tuple' => 2, + 'type' => 2, + 'unichr' => 2, + 'unicode' => 2, + 'vars' => 2, + 'xrange' => 2, + 'zip' => 2, + + 'ArithmeticError' => 3, + 'AssertionError' => 3, + 'AttributeError' => 3, + 'BaseException' => 3, + 'DeprecationWarning' => 3, + 'EOFError' => 3, + 'Ellipsis' => 3, + 'EnvironmentError' => 3, + 'Exception' => 3, + 'FloatingPointError' => 3, + 'FutureWarning' => 3, + 'GeneratorExit' => 3, + 'IOError' => 3, + 'ImportError' => 3, + 'ImportWarning' => 3, + 'IndentationError' => 3, + 'IndexError' => 3, + 'KeyError' => 3, + 'KeyboardInterrupt' => 3, + 'LookupError' => 3, + 'MemoryError' => 3, + 'NameError' => 3, + 'NotImplemented' => 3, + 'NotImplementedError' => 3, + 'OSError' => 3, + 'OverflowError' => 3, + 'OverflowWarning' => 3, + 'PendingDeprecationWarning' => 3, + 'ReferenceError' => 3, + 'RuntimeError' => 3, + 'RuntimeWarning' => 3, + 'StandardError' => 3, + 'StopIteration' => 3, + 'SyntaxError' => 3, + 'SyntaxWarning' => 3, + 'SystemError' => 3, + 'SystemExit' => 3, + 'TabError' => 3, + 'TypeError' => 3, + 'UnboundLocalError' => 3, + 'UnicodeDecodeError' => 3, + 'UnicodeEncodeError' => 3, + 'UnicodeError' => 3, + 'UnicodeTranslateError' => 3, + 'UnicodeWarning' => 3, + 'UserWarning' => 3, + 'ValueError' => 3, + 'Warning' => 3, + 'WindowsError' => 3, + 'ZeroDivisionError' => 3, + + 'BufferType' => 3, + 'BuiltinFunctionType' => 3, + 'BuiltinMethodType' => 3, + 'ClassType' => 3, + 'CodeType' => 3, + 'ComplexType' => 3, + 'DictProxyType' => 3, + 'DictType' => 3, + 'DictionaryType' => 3, + 'EllipsisType' => 3, + 'FileType' => 3, + 'FloatType' => 3, + 'FrameType' => 3, + 'FunctionType' => 3, + 'GeneratorType' => 3, + 'InstanceType' => 3, + 'IntType' => 3, + 'LambdaType' => 3, + 'ListType' => 3, + 'LongType' => 3, + 'MethodType' => 3, + 'ModuleType' => 3, + 'NoneType' => 3, + 'ObjectType' => 3, + 'SliceType' => 3, + 'StringType' => 3, + 'StringTypes' => 3, + 'TracebackType' => 3, + 'TupleType' => 3, + 'TypeType' => 3, + 'UnboundMethodType' => 3, + 'UnicodeType' => 3, + 'XRangeType' => 3, + + 'False' => 3, + 'None' => 3, + 'True' => 3, + + '__abs__' => 3, + '__add__' => 3, + '__all__' => 3, + '__author__' => 3, + '__bases__' => 3, + '__builtins__' => 3, + '__call__' => 3, + '__class__' => 3, + '__cmp__' => 3, + '__coerce__' => 3, + '__contains__' => 3, + '__debug__' => 3, + '__del__' => 3, + '__delattr__' => 3, + '__delitem__' => 3, + '__delslice__' => 3, + '__dict__' => 3, + '__div__' => 3, + '__divmod__' => 3, + '__doc__' => 3, + '__eq__' => 3, + '__file__' => 3, + '__float__' => 3, + '__floordiv__' => 3, + '__future__' => 3, + '__ge__' => 3, + '__getattr__' => 3, + '__getattribute__' => 3, + '__getitem__' => 3, + '__getslice__' => 3, + '__gt__' => 3, + '__hash__' => 3, + '__hex__' => 3, + '__iadd__' => 3, + '__import__' => 3, + '__imul__' => 3, + '__init__' => 3, + '__int__' => 3, + '__invert__' => 3, + '__iter__' => 3, + '__le__' => 3, + '__len__' => 3, + '__long__' => 3, + '__lshift__' => 3, + '__lt__' => 3, + '__members__' => 3, + '__metaclass__' => 3, + '__mod__' => 3, + '__mro__' => 3, + '__mul__' => 3, + '__name__' => 3, + '__ne__' => 3, + '__neg__' => 3, + '__new__' => 3, + '__nonzero__' => 3, + '__oct__' => 3, + '__or__' => 3, + '__path__' => 3, + '__pos__' => 3, + '__pow__' => 3, + '__radd__' => 3, + '__rdiv__' => 3, + '__rdivmod__' => 3, + '__reduce__' => 3, + '__repr__' => 3, + '__rfloordiv__' => 3, + '__rlshift__' => 3, + '__rmod__' => 3, + '__rmul__' => 3, + '__ror__' => 3, + '__rpow__' => 3, + '__rrshift__' => 3, + '__rsub__' => 3, + '__rtruediv__' => 3, + '__rxor__' => 3, + '__setattr__' => 3, + '__setitem__' => 3, + '__setslice__' => 3, + '__self__' => 3, + '__slots__' => 3, + '__str__' => 3, + '__sub__' => 3, + '__truediv__' => 3, + '__version__' => 3, + '__xor__' => 3 + ), + Generator::CASE_SENSITIVE + ); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Sql.php b/apigen/libs/FSHL/FSHL/Lexer/Sql.php new file mode 100644 index 00000000000..28f211e44c1 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Sql.php @@ -0,0 +1,557 @@ + array( + array( + 'ALPHA' => array('FUNCTION', Generator::BACK), + 'NUM' => array('NUMBER', Generator::NEXT), + 'DOTNUM' => array('NUMBER', Generator::NEXT), + '/*' => array('COMMENT_BLOCK', Generator::NEXT) , + '//' => array('COMMENT_LINE', Generator::NEXT), + '#' => array('COMMENT_LINE', Generator::NEXT), + '--' => array('COMMENT_LINE', Generator::NEXT), + '"' => array('QUOTE_DOUBLE', Generator::NEXT), + '\'' => array('QUOTE_SINGLE', Generator::NEXT), + '`' => array('QUOTE_BACK_APOSTROPHE', Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_KEYWORD, + null, + null + ), + 'FUNCTION' => array( + array( + '!ALNUM_' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_KEYWORD | Generator::STATE_FLAG_RECURSION, + null, + null + ), + 'COMMENT_BLOCK' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT), + '*/' => array(Generator::STATE_RETURN, Generator::CURRENT), + ), + Generator::STATE_FLAG_RECURSION, + 'sql-comment', + null + ), + 'COMMENT_LINE' => array( + array( + 'LINE' => array(Generator::STATE_RETURN, Generator::BACK), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-comment', + null + ), + 'QUOTE_DOUBLE' => array( + array( + '"' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\"' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-value', + null + ), + 'QUOTE_SINGLE' => array( + array( + '\'' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\\'' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-value', + null + ), + 'QUOTE_BACK_APOSTROPHE' => array( + array( + '`' => array(Generator::STATE_RETURN, Generator::CURRENT), + '\\`' => array(Generator::STATE_SELF, Generator::NEXT), + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + 'TAB' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-value', + null + ), + 'NUMBER' => array( + array( + 'x' => array('HEXA', Generator::NEXT), + 'DOTNUM' => array(Generator::STATE_SELF, Generator::NEXT), + 'ALL' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-num', + null + ), + 'HEXA' => array( + array( + '!HEXNUM' => array(Generator::STATE_RETURN, Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'sql-num', + null + ), + 'OPTION' => array( + array( + 'BLOB' => array(Generator::STATE_SELF, Generator::NEXT), + 'TEXT' => array(Generator::STATE_SELF, Generator::CURRENT), + 'INTEGER' => array(Generator::STATE_SELF, Generator::NEXT), + 'CHAR' => array(Generator::STATE_SELF, Generator::NEXT), + 'TEXT' => array(Generator::STATE_SELF, Generator::NEXT), + 'DATE' => array(Generator::STATE_SELF, Generator::NEXT) + ), + Generator::STATE_FLAG_RECURSION, + 'sql-option', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array( + 'sql-keyword', + array( + 'a' => 1, + 'abs' => 2, + 'acos' => 2, + 'add' => 1, + 'add_months' => 1, + 'after' => 1, + 'all' => 1, + 'alter' => 1, + 'an' => 1, + 'and' => 1, + 'any' => 1, + 'array' => 1, + 'as' => 1, + 'asc' => 1, + 'ascii' => 2, + 'asin' => 2, + 'atan' => 2, + 'atan2' => 2, + 'avg' => 2, + 'before' => 1, + 'begin' => 1, + 'between' => 1, + 'bigint' => 3, + 'binary' => 1, + 'bind' => 1, + 'binding' => 1, + 'bit' => 1, + 'blob' => 3, + 'boolean' => 3, + 'by' => 1, + 'call' => 1, + 'cascade' => 1, + 'case' => 1, + 'cast' => 1, + 'ceiling' => 2, + 'char' => 3, + 'char_length' => 2, + 'character' => 2, + 'character_length' => 2, + 'chartorowid' => 1, + 'check' => 1, + 'chr' => 1, + 'cleanup' => 1, + 'close' => 1, + 'clustered' => 1, + 'coalesce' => 1, + 'colgroup' => 1, + 'collate' => 1, + 'commit' => 1, + 'complex' => 1, + 'compress' => 1, + 'concat' => 2, + 'connect' => 1, + 'constraint' => 1, + 'contains' => 1, + 'continue' => 1, + 'convert' => 1, + 'cos' => 2, + 'count' => 2, + 'create' => 1, + 'cross' => 1, + 'curdate' => 2, + 'current' => 1, + 'cursor' => 1, + 'curtime' => 2, + 'cvar' => 1, + 'database' => 1, + 'datapages' => 1, + 'date' => 2, + 'dayname' => 2, + 'dayofmonth' => 2, + 'dayofweek' => 2, + 'dayofyear' => 2, + 'db_name' => 1, + 'dba' => 1, + 'dec' => 3, + 'decimal' => 3, + 'declaration' => 1, + 'declare' => 1, + 'decode' => 2, + 'default' => 1, + 'definition' => 1, + 'degrees' => 1, + 'delete' => 1, + 'desc' => 1, + 'describe' => 1, + 'descriptor' => 1, + 'dhtype' => 1, + 'difference' => 1, + 'distinct' => 1, + 'double' => 3, + 'drop' => 1, + 'each' => 1, + 'else' => 1, + 'end' => 1, + 'escape' => 1, + 'exclusive' => 1, + 'exec' => 1, + 'execute' => 1, + 'exists' => 1, + 'exit' => 1, + 'exp' => 2, + 'explicit' => 1, + 'extent' => 1, + 'fetch' => 1, + 'field file' => 1, + 'float' => 3, + 'floor' => 2, + 'for' => 1, + 'foreign' => 1, + 'found' => 1, + 'from' => 1, + 'full' => 1, + 'go' => 1, + 'goto' => 1, + 'grant' => 1, + 'greatest' => 2, + 'group' => 1, + 'hash' => 1, + 'having' => 1, + 'hour' => 1, + 'identified' => 1, + 'ifnull' => 2, + 'immediate' => 1, + 'in' => 1, + 'index' => 1, + 'indexpages' => 1, + 'indicator' => 1, + 'initcap' => 1, + 'inner' => 1, + 'inout' => 1, + 'input' => 1, + 'insert' => 1, + 'instr' => 1, + 'int' => 3, + 'integer' => 3, + 'interface' => 1, + 'intersect' => 1, + 'into' => 1, + 'is' => 1, + 'isnull' => 2, + 'java_object' => 3, + 'join' => 1, + 'key' => 1, + 'last_day' => 2, + 'lcase' => 2, + 'least' => 2, + 'left' => 2, + 'length' => 2, + 'like' => 1, + 'link' => 1, + 'list' => 1, + 'locate' => 1, + 'lock' => 1, + 'log' => 2, + 'log10' => 2, + 'long' => 1, + 'longblob' => 3, + 'longtext' => 3, + 'longvarbinary' => 3, + 'longvarchar' => 3, + 'lower' => 1, + 'lpad' => 1, + 'ltrim' => 2, + 'lvarbinary' => 1, + 'lvarchar' => 1, + 'main' => 1, + 'max' => 2, + 'mediumint' => 3, + 'metadata_only' => 1, + 'min' => 2, + 'minus' => 2, + 'minute' => 2, + 'mod' => 2, + 'mode' => 1, + 'modify' => 1, + 'money' => 1, + 'month' => 2, + 'monthname' => 2, + 'months_between' => 2, + 'name' => 1, + 'national' => 1, + 'natural' => 1, + 'nchar' => 1, + 'newrow' => 1, + 'next_day' => 1, + 'nocompress' => 1, + 'not' => 1, + 'now' => 1, + 'nowait' => 1, + 'null' => 1, + 'nullif' => 1, + 'nullvalue' => 1, + 'number' => 1, + 'numeric' => 1, + 'nvl' => 1, + 'object_id' => 1, + 'odbc_convert' => 1, + 'odbcinfo' => 1, + 'of' => 1, + 'oldrow' => 1, + 'on' => 1, + 'open' => 1, + 'option' => 1, + 'or' => 1, + 'order' => 1, + 'out' => 1, + 'outer' => 1, + 'output' => 1, + 'pctfree' => 1, + 'pi' => 1, + 'power' => 1, + 'precision' => 1, + 'prefix' => 1, + 'prepare' => 1, + 'primary' => 1, + 'privileges' => 1, + 'procedure' => 1, + 'public' => 1, + 'quarter' => 2, + 'radians' => 2, + 'rand' => 2, + 'range' => 2, + 'raw' => 1, + 'real' => 3, + 'record' => 1, + 'references' => 1, + 'referencing' => 1, + 'rename' => 1, + 'repeat' => 2, + 'replace' => 1, + 'resource' => 1, + 'restrict' => 1, + 'result' => 1, + 'return' => 2, + 'revoke' => 2, + 'right' => 2, + 'rollback' => 1, + 'row' => 2, + 'rowid' => 2, + 'rowidtochar' => 2, + 'rownum' => 2, + 'rpad' => 2, + 'rtrim' => 2, + 'searched_case' => 1, + 'second' => 1, + 'section' => 1, + 'select' => 1, + 'service' => 1, + 'set' => 1, + 'share' => 1, + 'short' => 1, + 'sign' => 1, + 'simple_case' => 1, + 'sin' => 2, + 'size' => 2, + 'smallint' => 3, + 'some' => 1, + 'soundex' => 1, + 'space' => 1, + 'sql' => 1, + 'sql_bigint' => 3, + 'sql_binary' => 3, + 'sql_bit' => 3, + 'sql_char' => 3, + 'sql_date' => 3, + 'sql_decimal' => 3, + 'sql_double' => 3, + 'sql_float' => 1, + 'sql_integer' => 3, + 'sql_longvarbinary' => 3, + 'sql_longvarchar' => 3, + 'sql_numeric' => 3, + 'sql_real' => 3, + 'sql_smallint' => 3, + 'sql_time' => 3, + 'sql_timestamp' => 1, + 'sql_tinyint' => 3, + 'sql_tsi_day' => 3, + 'sql_tsi_frac_second' => 3, + 'sql_tsi_hour' => 3, + 'sql_tsi_minute' => 3, + 'sql_tsi_month' => 3, + 'sql_tsi_quarter' => 3, + 'sql_tsi_second' => 3, + 'sql_tsi_week' => 3, + 'sql_tsi_year' => 3, + 'sql_varbinary' => 3, + 'sql_varchar' => 3, + 'sqlerror' => 1, + 'sqlwarning' => 1, + 'sqrt' => 1, + 'start' => 1, + 'statement' => 1, + 'statistics' => 1, + 'stop' => 1, + 'storage_attributes' => 1, + 'storage_manager' => 1, + 'store_in_progress' => 1, + 'string' => 3, + 'substr' => 2, + 'substring' => 2, + 'suffix' => 2, + 'sum' => 2, + 'suser_name' => 2, + 'synonym' => 2, + 'sysdate' => 2, + 'systime' => 2, + 'systimestamp' => 2, + 'table' => 1, + 'tan' => 2, + 'text' => 3, + 'then' => 1, + 'time' => 2, + 'timeout' => 2, + 'timestamp' => 3, + 'timestampadd' => 2, + 'timestampdiff' => 2, + 'tinyint' => 3, + 'to' => 2, + 'to_char' => 2, + 'to_date' => 2, + 'to_number' => 2, + 'to_time' => 2, + 'to_timestamp' => 2, + 'top' => 1, + 'tpe' => 1, + 'tran' => 1, + 'transaction' => 1, + 'translate' => 1, + 'trigger' => 1, + 'type' => 1, + 'ucase' => 1, + 'uid' => 1, + 'union' => 1, + 'unique' => 1, + 'unsigned' => 1, + 'update' => 1, + 'upper' => 1, + 'user' => 1, + 'user_id' => 1, + 'user_name' => 1, + 'using' => 1, + 'uuid' => 1, + 'values' => 1, + 'varbinary' => 1, + 'varchar' => 3, + 'variables' => 1, + 'varying' => 1, + 'version' => 1, + 'view' => 1, + 'week' => 2, + 'when' => 1, + 'whenever' => 1, + 'where' => 1, + 'with' => 1, + 'work' => 1, + 'year' => 1 + ), + Generator::CASE_INSENSITIVE + ); + } +} diff --git a/apigen/libs/FSHL/FSHL/Lexer/Texy.php b/apigen/libs/FSHL/FSHL/Lexer/Texy.php new file mode 100644 index 00000000000..bea063e99d3 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Lexer/Texy.php @@ -0,0 +1,297 @@ + array( + array( + '/---' => array('BLOCK_IN', Generator::NEXT), + '\---' => array('BLOCK_OUT', Generator::NEXT), + 'LINE' => array('LINE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'LINE' => array( + array( + 'LINE' => array('LINE_DOUBLE', Generator::NEXT), + '!SPACE' => array('LINE_SINGLE', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'LINE_SINGLE' => array( + array( + '##' => array('HEADER_IN', Generator::NEXT), + '**' => array('HEADER_IN', Generator::NEXT), + '==' => array('HEADER_IN', Generator::NEXT), + '--' => array('HEADER_IN', Generator::NEXT), + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + null, + null + ), + 'LINE_DOUBLE' => array( + array( + 'LINE' => array(Generator::STATE_SELF, Generator::NEXT), + '##' => array('HEADER_IN', Generator::NEXT), + '==' => array('HEADER_IN', Generator::NEXT), + '--' => array('HORIZONTAL_LINE', Generator::NEXT), + '- -' => array('HORIZONTAL_LINE', Generator::NEXT), + '**' => array('HORIZONTAL_LINE', Generator::NEXT), + '* *' => array('HORIZONTAL_LINE', Generator::NEXT), + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + 'texy-err', + null, + null + ), + 'HEADER_IN' => array( + array( + '=' => array('HEADER_IN', Generator::NEXT), + '#' => array('HEADER_IN', Generator::NEXT), + '-' => array('HEADER_IN', Generator::NEXT), + '*' => array('HEADER_IN', Generator::NEXT), + 'LINE' => array('LINE_DOUBLE', Generator::NEXT), + 'ALL' => array('HEADER_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hlead', + null + ), + 'HEADER_BODY' => array( + array( + '=' => array('HEADER_OUT', Generator::NEXT), + '#' => array('HEADER_OUT', Generator::NEXT), + '-' => array('HEADER_OUT', Generator::NEXT), + '*' => array('HEADER_OUT', Generator::NEXT), + 'LINE' => array('LINE_DOUBLE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'texy-hbody', + null + ), + 'HEADER_OUT' => array( + array( + 'LINE' => array('LINE_DOUBLE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'texy-hlead', + null + ), + 'HORIZONTAL_LINE' => array( + array( + 'LINE' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_IN' => array( + array( + 'html' => array('BLOCK_HTML', Generator::NEXT), + 'code' => array('BLOCK_CODE', Generator::NEXT), + 'div' => array('BLOCK_DUMMY', Generator::NEXT), + 'text' => array('BLOCK_TEXT', Generator::NEXT), + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_OUT' => array( + array( + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_DUMMY' => array( + array( + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_TEXT' => array( + array( + 'LINE' => array('BLOCK_TEXT_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_TEXT_BODY' => array( + array( + 'LINE' => array('BLOCK_TEXT_BODY_LINE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'texy-text', + null + ), + 'BLOCK_TEXT_BODY_LINE' => array( + array( + '\---' => array('BLOCK_TEXT_BODY_OUT', Generator::NEXT), + 'ALL' => array('BLOCK_TEXT_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-text', + null + ), + 'BLOCK_TEXT_BODY_OUT' => array( + array( + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_HTML' => array( + array( + 'LINE' => array('BLOCK_HTML_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_HTML_BODY' => array( + array( + 'LINE' => array('BLOCK_HTML_BODY_LINE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'texy-html', + null + ), + 'BLOCK_HTML_BODY_LINE' => array( + array( + '\---' => array('BLOCK_HTML_BODY_OUT', Generator::NEXT), + 'ALL' => array('BLOCK_HTML_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-html', + null + ), + 'BLOCK_HTML_BODY_OUT' => array( + array( + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_CODE' => array( + array( + 'LINE' => array('BLOCK_CODE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ), + 'BLOCK_CODE_BODY' => array( + array( + 'LINE' => array('BLOCK_CODE_BODY_LINE', Generator::NEXT) + ), + Generator::STATE_FLAG_NONE, + 'texy-code', + null + ), + 'BLOCK_CODE_BODY_LINE' => array( + array( + '\---' => array('BLOCK_CODE_BODY_OUT', Generator::NEXT), + 'ALL' => array('BLOCK_CODE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-code', + null + ), + 'BLOCK_CODE_BODY_OUT' => array( + array( + 'ALL' => array('LINE_BODY', Generator::BACK) + ), + Generator::STATE_FLAG_NONE, + 'texy-hr', + null + ) + ); + } + + /** + * Returns special delimiters. + * + * @return array + */ + public function getDelimiters() + { + return array(); + } + + /** + * Returns keywords. + * + * @return array + */ + public function getKeywords() + { + return array(); + } +} diff --git a/apigen/libs/FSHL/FSHL/Output.php b/apigen/libs/FSHL/FSHL/Output.php new file mode 100644 index 00000000000..8f5def42f42 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Output.php @@ -0,0 +1,48 @@ +lastClass !== $class) { + if (null !== $this->lastClass) { + $output .= ''; + } + if (null !== $class) { + $output .= ''; + } + + $this->lastClass = $class; + } + + return $output . htmlspecialchars($part, ENT_COMPAT, 'UTF-8'); + } + + /** + * Outputs a keyword. + * + * @param string $part + * @param string $class + * @return string + */ + public function keyword($part, $class) + { + $output = ''; + + if ($this->lastClass !== $class) { + if (null !== $this->lastClass) { + $output .= ''; + } + if (null !== $class) { + $output .= ''; + } + + $this->lastClass = $class; + } + + return $output . htmlspecialchars($part, ENT_COMPAT, 'UTF-8'); + } +} \ No newline at end of file diff --git a/apigen/libs/FSHL/FSHL/Output/HtmlManual.php b/apigen/libs/FSHL/FSHL/Output/HtmlManual.php new file mode 100644 index 00000000000..dadecd1a660 --- /dev/null +++ b/apigen/libs/FSHL/FSHL/Output/HtmlManual.php @@ -0,0 +1,123 @@ + 'http://php.net/manual/en/langref.php', + 'php-keyword2' => 'http://php.net/%s', + + 'sql-keyword1' => 'http://search.oracle.com/search/search?group=MySQL&q=%s', + 'sql-keyword2' => 'http://search.oracle.com/search/search?group=MySQL&q=%s', + 'sql-keyword3' => 'http://search.oracle.com/search/search?group=MySQL&q=%s', + ); + + /** + * Outputs a template part. + * + * @param string $part + * @param string $class + * @return string + */ + public function template($part, $class) + { + $output = ''; + + if ($this->lastClass !== $class) { + if (null !== $this->lastClass) { + $output .= ''; + } + + $output .= $this->closeTag; + $this->closeTag = ''; + + if (null !== $class) { + $output .= ''; + } + + $this->lastClass = $class; + } + + return $output . htmlspecialchars($part, ENT_COMPAT, 'UTF-8'); + } + + /** + * Outputs a keyword. + * + * @param string $part + * @param string $class + * @return string + */ + public function keyword($part, $class) + { + $output = ''; + + if ($this->lastClass !== $class) { + if (null !== $this->lastClass) { + $output .= ''; + } + + $output .= $this->closeTag; + $this->closeTag = ''; + + if (null !== $class) { + if (isset($this->manualUrl[$class])) { + $output .= ''; + $this->closeTag = ''; + } + + $output .= ''; + } + + $this->lastClass = $class; + } + + return $output . htmlspecialchars($part, ENT_COMPAT, 'UTF-8'); + } +} \ No newline at end of file diff --git a/apigen/libs/FSHL/LICENSE b/apigen/libs/FSHL/LICENSE new file mode 100644 index 00000000000..ed03923cfe5 --- /dev/null +++ b/apigen/libs/FSHL/LICENSE @@ -0,0 +1,19 @@ +License +======= + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + + +GNU General Public License +-------------------------- + +GPL license is very very long, so instead of including it here we offer +you URL with full text: http://www.gnu.org/licenses/gpl-2.0.html diff --git a/apigen/libs/Nette/Nette/Application/Application.php b/apigen/libs/Nette/Nette/Application/Application.php new file mode 100644 index 00000000000..5ad4cacab48 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Application.php @@ -0,0 +1,262 @@ +httpRequest = $httpRequest; + $this->httpResponse = $httpResponse; + $this->presenterFactory = $presenterFactory; + $this->router = $router; + } + + + + /** + * Dispatch a HTTP request to a front controller. + * @return void + */ + public function run() + { + $request = NULL; + $repeatedError = FALSE; + do { + try { + if (count($this->requests) > self::$maxLoop) { + throw new ApplicationException('Too many loops detected in application life cycle.'); + } + + if (!$request) { + $this->onStartup($this); + + $request = $this->router->match($this->httpRequest); + if (!$request instanceof Request) { + $request = NULL; + throw new BadRequestException('No route for HTTP request.'); + } + + if (strcasecmp($request->getPresenterName(), $this->errorPresenter) === 0) { + throw new BadRequestException('Invalid request. Presenter is not achievable.'); + } + } + + $this->requests[] = $request; + $this->onRequest($this, $request); + + // Instantiate presenter + $presenterName = $request->getPresenterName(); + try { + $this->presenter = $this->presenterFactory->createPresenter($presenterName); + } catch (InvalidPresenterException $e) { + throw new BadRequestException($e->getMessage(), 404, $e); + } + + $this->presenterFactory->getPresenterClass($presenterName); + $request->setPresenterName($presenterName); + $request->freeze(); + + // Execute presenter + $response = $this->presenter->run($request); + if ($response) { + $this->onResponse($this, $response); + } + + // Send response + if ($response instanceof Responses\ForwardResponse) { + $request = $response->getRequest(); + continue; + + } elseif ($response instanceof IResponse) { + $response->send($this->httpRequest, $this->httpResponse); + } + break; + + } catch (\Exception $e) { + // fault barrier + $this->onError($this, $e); + + if (!$this->catchExceptions) { + $this->onShutdown($this, $e); + throw $e; + } + + if ($repeatedError) { + $e = new ApplicationException('An error occurred while executing error-presenter', 0, $e); + } + + if (!$this->httpResponse->isSent()) { + $this->httpResponse->setCode($e instanceof BadRequestException ? $e->getCode() : 500); + } + + if (!$repeatedError && $this->errorPresenter) { + $repeatedError = TRUE; + if ($this->presenter instanceof UI\Presenter) { + try { + $this->presenter->forward(":$this->errorPresenter:", array('exception' => $e)); + } catch (AbortException $foo) { + $request = $this->presenter->getLastCreatedRequest(); + } + } else { + $request = new Request( + $this->errorPresenter, + Request::FORWARD, + array('exception' => $e) + ); + } + // continue + + } else { // default error handler + if ($e instanceof BadRequestException) { + $code = $e->getCode(); + } else { + $code = 500; + Nette\Diagnostics\Debugger::log($e, Nette\Diagnostics\Debugger::ERROR); + } + require __DIR__ . '/templates/error.phtml'; + break; + } + } + } while (1); + + $this->onShutdown($this, isset($e) ? $e : NULL); + } + + + + /** + * Returns all processed requests. + * @return Request[] + */ + final public function getRequests() + { + return $this->requests; + } + + + + /** + * Returns current presenter. + * @return IPresenter + */ + final public function getPresenter() + { + return $this->presenter; + } + + + + /********************* services ****************d*g**/ + + + + /** + * Returns router. + * @return IRouter + */ + public function getRouter() + { + return $this->router; + } + + + + /** + * Returns presenter factory. + * @return IPresenterFactory + */ + public function getPresenterFactory() + { + return $this->presenterFactory; + } + + + + /********************* request serialization ****************d*g**/ + + + + /** @deprecated */ + function storeRequest($expiration = '+ 10 minutes') + { + return $this->presenter->storeRequest($expiration); + } + + /** @deprecated */ + function restoreRequest($key) + { + return $this->presenter->restoreRequest($key); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Diagnostics/RoutingPanel.php b/apigen/libs/Nette/Nette/Application/Diagnostics/RoutingPanel.php new file mode 100644 index 00000000000..147abf1aaf7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Diagnostics/RoutingPanel.php @@ -0,0 +1,125 @@ +addPanel(function($e) use ($application) { + return $e ? NULL : array( + 'tab' => 'Nette Application', + 'panel' => '

Requests

' . Nette\Diagnostics\Helpers::clickableDump($application->getRequests()) + . '

Presenter

' . Nette\Diagnostics\Helpers::clickableDump($application->getPresenter()) + ); + }); + } + + + + public function __construct(Nette\Application\IRouter $router, Nette\Http\IRequest $httpRequest) + { + $this->router = $router; + $this->httpRequest = $httpRequest; + } + + + + /** + * Renders tab. + * @return string + */ + public function getTab() + { + $this->analyse($this->router); + ob_start(); + require __DIR__ . '/templates/RoutingPanel.tab.phtml'; + return ob_get_clean(); + } + + + + /** + * Renders panel. + * @return string + */ + public function getPanel() + { + ob_start(); + require __DIR__ . '/templates/RoutingPanel.panel.phtml'; + return ob_get_clean(); + } + + + + /** + * Analyses simple route. + * @param Nette\Application\IRouter + * @return void + */ + private function analyse($router, $module = '') + { + if ($router instanceof Routers\RouteList) { + foreach ($router as $subRouter) { + $this->analyse($subRouter, $module . $router->getModule()); + } + return; + } + + $matched = 'no'; + $request = $router->match($this->httpRequest); + if ($request) { + $request->setPresenterName($module . $request->getPresenterName()); + $matched = 'may'; + if (empty($this->request)) { + $this->request = $request; + $matched = 'yes'; + } + } + + $this->routers[] = array( + 'matched' => $matched, + 'class' => get_class($router), + 'defaults' => $router instanceof Routers\Route || $router instanceof Routers\SimpleRouter ? $router->getDefaults() : array(), + 'mask' => $router instanceof Routers\Route ? $router->getMask() : NULL, + 'request' => $request, + 'module' => rtrim($module, ':') + ); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml b/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml new file mode 100644 index 00000000000..0ecf172f05a --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.panel.phtml @@ -0,0 +1,119 @@ + + + +
+

+request)): ?> + no route + + request->getPresenterName() . ':' . (isset($this->request->parameters[Presenter::ACTION_KEY]) ? $this->request->parameters[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->parameters[Presenter::SIGNAL_KEY]) ? " {$this->request->parameters[Presenter::SIGNAL_KEY]}!" : '')) ?> + +

+ +request)): ?> + request->getParameters() ?> + +

No parameters.

+ + + + + + + + + + + + $value): ?> + + + + + + +
ParameterValue
+ + + +

Routers

+ +routers)): ?> +

No routers defined.

+ + +
+ + + + + + + + + + + + + + routers as $router): ?> + + + + + + + + + + + + + + + +
Matched?ClassMaskDefaultsModuleRequest
+ $value): ?> +
+ +
+ getParameters(); ?> + getPresenterName() . ':' . (isset($params[Presenter::ACTION_KEY]) ? $params[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION)) ?>
+ + $value): ?> +
+ +
+
+ +
diff --git a/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml b/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml new file mode 100644 index 00000000000..9edf58215f3 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Diagnostics/templates/RoutingPanel.tab.phtml @@ -0,0 +1,10 @@ + +request)): ?>no routerequest->getPresenterName() . ':' . (isset($this->request->parameters[Presenter::ACTION_KEY]) ? $this->request->parameters[Presenter::ACTION_KEY] : Presenter::DEFAULT_ACTION) . (isset($this->request->parameters[Presenter::SIGNAL_KEY]) ? " {$this->request->parameters[Presenter::SIGNAL_KEY]}!" : '')); endif ?> diff --git a/apigen/libs/Nette/Nette/Application/IPresenter.php b/apigen/libs/Nette/Nette/Application/IPresenter.php new file mode 100644 index 00000000000..2ed245861d1 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/IPresenter.php @@ -0,0 +1,32 @@ + + */ +interface IPresenterFactory +{ + + /** + * @param string presenter name + * @return string class name + * @throws InvalidPresenterException + */ + function getPresenterClass(& $name); + + /** + * Create new presenter instance. + * @param string presenter name + * @return IPresenter + */ + function createPresenter($name); + +} diff --git a/apigen/libs/Nette/Nette/Application/IResponse.php b/apigen/libs/Nette/Nette/Application/IResponse.php new file mode 100644 index 00000000000..63d9810b6dc --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/IResponse.php @@ -0,0 +1,32 @@ +context = $context; + } + + + + /** + * Gets the context. + * @return \SystemContainer|Nette\DI\Container + */ + final public function getContext() + { + return $this->context; + } + + + + /** + * @param Nette\Application\Request + * @return Nette\Application\IResponse + */ + public function run(Application\Request $request) + { + $this->request = $request; + + $httpRequest = $this->context->getByType('Nette\Http\IRequest'); + if (!$httpRequest->isAjax() && ($request->isMethod('get') || $request->isMethod('head'))) { + $refUrl = clone $httpRequest->getUrl(); + $url = $this->context->router->constructUrl($request, $refUrl->setPath($refUrl->getScriptPath())); + if ($url !== NULL && !$httpRequest->getUrl()->isEqual($url)) { + return new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY); + } + } + + $params = $request->getParameters(); + if (!isset($params['callback'])) { + return; + } + $params['presenter'] = $this; + $callback = new Nette\Callback($params['callback']); + $response = $callback->invokeArgs(Application\UI\PresenterComponentReflection::combineArgs($callback->toReflection(), $params)); + + if (is_string($response)) { + $response = array($response, array()); + } + if (is_array($response)) { + if ($response[0] instanceof \SplFileInfo) { + $response = $this->createTemplate('Nette\Templating\FileTemplate') + ->setParameters($response[1])->setFile($response[0]); + } else { + $response = $this->createTemplate('Nette\Templating\Template') + ->setParameters($response[1])->setSource($response[0]); + } + } + if ($response instanceof Nette\Templating\ITemplate) { + return new Responses\TextResponse($response); + } else { + return $response; + } + } + + + + /** + * Template factory. + * @param string + * @param callable + * @return Nette\Templating\ITemplate + */ + public function createTemplate($class = NULL, $latteFactory = NULL) + { + $template = $class ? new $class : new Nette\Templating\FileTemplate; + + $template->setParameters($this->request->getParameters()); + $template->presenter = $this; + $template->context = $context = $this->context; + $url = $context->getByType('Nette\Http\IRequest')->getUrl(); + $template->baseUrl = rtrim($url->getBaseUrl(), '/'); + $template->basePath = rtrim($url->getBasePath(), '/'); + + $template->registerHelperLoader('Nette\Templating\Helpers::loader'); + $template->setCacheStorage($context->nette->templateCacheStorage); + $template->onPrepareFilters[] = function($template) use ($latteFactory, $context) { + $template->registerFilter($latteFactory ? $latteFactory() : new Nette\Latte\Engine); + }; + return $template; + } + + + + /** + * Redirects to another URL. + * @param string + * @param int HTTP code + * @return void + */ + public function redirectUrl($url, $code = Http\IResponse::S302_FOUND) + { + return new Responses\RedirectResponse($url, $code); + } + + + + /** + * Throws HTTP error. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\BadRequestException + */ + public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND) + { + throw new Application\BadRequestException($message, $code); + } + + + + /** + * @return Nette\Application\IRequest + */ + public function getRequest() + { + return $this->request; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/PresenterFactory.php b/apigen/libs/Nette/Nette/Application/PresenterFactory.php new file mode 100644 index 00000000000..bc0cbfe12ed --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/PresenterFactory.php @@ -0,0 +1,171 @@ +baseDir = $baseDir; + $this->container = $container; + } + + + + /** + * Create new presenter instance. + * @param string presenter name + * @return IPresenter + */ + public function createPresenter($name) + { + $presenter = $this->container->createInstance($this->getPresenterClass($name)); + if (method_exists($presenter, 'setContext')) { + $this->container->callMethod(array($presenter, 'setContext')); + } + foreach (array_reverse(get_class_methods($presenter)) as $method) { + if (substr($method, 0, 6) === 'inject') { + $this->container->callMethod(array($presenter, $method)); + } + } + + if ($presenter instanceof UI\Presenter && $presenter->invalidLinkMode === NULL) { + $presenter->invalidLinkMode = $this->container->parameters['debugMode'] ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT; + } + return $presenter; + } + + + + /** + * @param string presenter name + * @return string class name + * @throws InvalidPresenterException + */ + public function getPresenterClass(& $name) + { + if (isset($this->cache[$name])) { + list($class, $name) = $this->cache[$name]; + return $class; + } + + if (!is_string($name) || !Nette\Utils\Strings::match($name, "#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#")) { + throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid."); + } + + $class = $this->formatPresenterClass($name); + + if (!class_exists($class)) { + // internal autoloading + $file = $this->formatPresenterFile($name); + if (is_file($file) && is_readable($file)) { + Nette\Utils\LimitedScope::load($file, TRUE); + } + + if (!class_exists($class)) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found in '$file'."); + } + } + + $reflection = new Nette\Reflection\ClassType($class); + $class = $reflection->getName(); + + if (!$reflection->implementsInterface('Nette\Application\IPresenter')) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor."); + } + + if ($reflection->isAbstract()) { + throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract."); + } + + // canonicalize presenter name + $realName = $this->unformatPresenterClass($class); + if ($name !== $realName) { + if ($this->caseSensitive) { + throw new InvalidPresenterException("Cannot load presenter '$name', case mismatch. Real name is '$realName'."); + } else { + $this->cache[$name] = array($class, $realName); + $name = $realName; + } + } else { + $this->cache[$name] = array($class, $realName); + } + + return $class; + } + + + + /** + * Formats presenter class name from its name. + * @param string + * @return string + */ + public function formatPresenterClass($presenter) + { + /*5.2*return strtr($presenter, ':', '_') . 'Presenter';*/ + return str_replace(':', 'Module\\', $presenter) . 'Presenter'; + } + + + + /** + * Formats presenter name from class name. + * @param string + * @return string + */ + public function unformatPresenterClass($class) + { + /*5.2*return strtr(substr($class, 0, -9), '_', ':');*/ + return str_replace('Module\\', ':', substr($class, 0, -9)); + } + + + + /** + * Formats presenter class file name. + * @param string + * @return string + */ + public function formatPresenterFile($presenter) + { + $path = '/' . str_replace(':', 'Module/', $presenter); + return $this->baseDir . substr_replace($path, '/presenters', strrpos($path, '/'), 0) . 'Presenter.php'; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Request.php b/apigen/libs/Nette/Nette/Application/Request.php new file mode 100644 index 00000000000..b4b6c4a5b03 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Request.php @@ -0,0 +1,270 @@ +name = $name; + $this->method = $method; + $this->params = $params; + $this->post = $post; + $this->files = $files; + $this->flags = $flags; + } + + + + /** + * Sets the presenter name. + * @param string + * @return Request provides a fluent interface + */ + public function setPresenterName($name) + { + $this->updating(); + $this->name = $name; + return $this; + } + + + + /** + * Retrieve the presenter name. + * @return string + */ + public function getPresenterName() + { + return $this->name; + } + + + + /** + * Sets variables provided to the presenter. + * @param array + * @return Request provides a fluent interface + */ + public function setParameters(array $params) + { + $this->updating(); + $this->params = $params; + return $this; + } + + + + /** + * Returns all variables provided to the presenter (usually via URL). + * @return array + */ + public function getParameters() + { + return $this->params; + } + + + + /** @deprecated */ + function setParams(array $params) + { + trigger_error(__METHOD__ . '() is deprecated; use setParameters() instead.', E_USER_WARNING); + return $this->setParameters($params); + } + + + + /** @deprecated */ + function getParams() + { + trigger_error(__METHOD__ . '() is deprecated; use getParameters() instead.', E_USER_WARNING); + return $this->getParameters(); + } + + + + /** + * Sets variables provided to the presenter via POST. + * @param array + * @return Request provides a fluent interface + */ + public function setPost(array $params) + { + $this->updating(); + $this->post = $params; + return $this; + } + + + + /** + * Returns all variables provided to the presenter via POST. + * @return array + */ + public function getPost() + { + return $this->post; + } + + + + /** + * Sets all uploaded files. + * @param array + * @return Request provides a fluent interface + */ + public function setFiles(array $files) + { + $this->updating(); + $this->files = $files; + return $this; + } + + + + /** + * Returns all uploaded files. + * @return array + */ + public function getFiles() + { + return $this->files; + } + + + + /** + * Sets the method. + * @param string + * @return Request provides a fluent interface + */ + public function setMethod($method) + { + $this->method = $method; + return $this; + } + + + + /** + * Returns the method. + * @return string + */ + public function getMethod() + { + return $this->method; + } + + + + /** + * Checks if the method is the given one. + * @param string + * @return bool + */ + public function isMethod($method) + { + return strcasecmp($this->method, $method) === 0; + } + + + + /** + * Checks if the method is POST. + * @return bool + */ + public function isPost() + { + return strcasecmp($this->method, 'post') === 0; + } + + + + /** + * Sets the flag. + * @param string + * @param bool + * @return Request provides a fluent interface + */ + public function setFlag($flag, $value = TRUE) + { + $this->updating(); + $this->flags[$flag] = (bool) $value; + return $this; + } + + + + /** + * Checks the flag. + * @param string + * @return bool + */ + public function hasFlag($flag) + { + return !empty($this->flags[$flag]); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Responses/FileResponse.php b/apigen/libs/Nette/Nette/Application/Responses/FileResponse.php new file mode 100644 index 00000000000..298a2b69462 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Responses/FileResponse.php @@ -0,0 +1,139 @@ +file = $file; + $this->name = $name ? $name : basename($file); + $this->contentType = $contentType ? $contentType : 'application/octet-stream'; + } + + + + /** + * Returns the path to a downloaded file. + * @return string + */ + final public function getFile() + { + return $this->file; + } + + + + /** + * Returns the file name. + * @return string + */ + final public function getName() + { + return $this->name; + } + + + + /** + * Returns the MIME content type of a downloaded file. + * @return string + */ + final public function getContentType() + { + return $this->contentType; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + $httpResponse->setContentType($this->contentType); + $httpResponse->setHeader('Content-Disposition', 'attachment; filename="' . $this->name . '"'); + + $filesize = $length = filesize($this->file); + $handle = fopen($this->file, 'r'); + + if ($this->resuming) { + $httpResponse->setHeader('Accept-Ranges', 'bytes'); + if (preg_match('#^bytes=(\d*)-(\d*)$#', $httpRequest->getHeader('Range'), $matches)) { + list(, $start, $end) = $matches; + if ($start === '') { + $start = max(0, $filesize - $end); + $end = $filesize - 1; + + } elseif ($end === '' || $end > $filesize - 1) { + $end = $filesize - 1; + } + if ($end <= $start) { + $httpResponse->setCode(416); // requested range not satisfiable + return; + } + + $httpResponse->setCode(206); + $httpResponse->setHeader('Content-Range', 'bytes ' . $start . '-' . $end . '/' . $filesize); + $length = $end - $start + 1; + fseek($handle, $start); + + } else { + $httpResponse->setHeader('Content-Range', 'bytes 0-' . ($filesize - 1) . '/' . $filesize); + } + } + + $httpResponse->setHeader('Content-Length', $length); + while (!feof($handle) && $length > 0) { + echo $s = fread($handle, min(4e6, $length)); + $length -= strlen($s); + } + fclose($handle); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Responses/ForwardResponse.php b/apigen/libs/Nette/Nette/Application/Responses/ForwardResponse.php new file mode 100644 index 00000000000..edfa1d80e1c --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Responses/ForwardResponse.php @@ -0,0 +1,60 @@ +request = $request; + } + + + + /** + * @return Nette\Application\Request + */ + final public function getRequest() + { + return $this->request; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Responses/JsonResponse.php b/apigen/libs/Nette/Nette/Application/Responses/JsonResponse.php new file mode 100644 index 00000000000..1002de9e705 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Responses/JsonResponse.php @@ -0,0 +1,83 @@ +payload = $payload; + $this->contentType = $contentType ? $contentType : 'application/json'; + } + + + + /** + * @return array|\stdClass + */ + final public function getPayload() + { + return $this->payload; + } + + + + /** + * Returns the MIME content type of a downloaded file. + * @return string + */ + final public function getContentType() + { + return $this->contentType; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + $httpResponse->setContentType($this->contentType); + $httpResponse->setExpiration(FALSE); + echo Nette\Utils\Json::encode($this->payload); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Responses/RedirectResponse.php b/apigen/libs/Nette/Nette/Application/Responses/RedirectResponse.php new file mode 100644 index 00000000000..92d7ce9064a --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Responses/RedirectResponse.php @@ -0,0 +1,78 @@ +url = (string) $url; + $this->code = (int) $code; + } + + + + /** + * @return string + */ + final public function getUrl() + { + return $this->url; + } + + + + /** + * @return int + */ + final public function getCode() + { + return $this->code; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Http\IRequest $httpRequest, Http\IResponse $httpResponse) + { + $httpResponse->redirect($this->url, $this->code); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Responses/TextResponse.php b/apigen/libs/Nette/Nette/Application/Responses/TextResponse.php new file mode 100644 index 00000000000..dbd3138fff8 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Responses/TextResponse.php @@ -0,0 +1,66 @@ +source = $source; + } + + + + /** + * @return mixed + */ + final public function getSource() + { + return $this->source; + } + + + + /** + * Sends response to output. + * @return void + */ + public function send(Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) + { + if ($this->source instanceof Nette\Templating\ITemplate) { + $this->source->render(); + + } else { + echo $this->source; + } + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Routers/CliRouter.php b/apigen/libs/Nette/Nette/Application/Routers/CliRouter.php new file mode 100644 index 00000000000..5c842edf77d --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Routers/CliRouter.php @@ -0,0 +1,129 @@ +defaults = $defaults; + } + + + + /** + * Maps command line arguments to a Request object. + * @param Nette\Http\IRequest + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + if (empty($_SERVER['argv']) || !is_array($_SERVER['argv'])) { + return NULL; + } + + $names = array(self::PRESENTER_KEY); + $params = $this->defaults; + $args = $_SERVER['argv']; + array_shift($args); + $args[] = '--'; + + foreach ($args as $arg) { + $opt = preg_replace('#/|-+#A', '', $arg); + if ($opt === $arg) { + if (isset($flag) || $flag = array_shift($names)) { + $params[$flag] = $arg; + } else { + $params[] = $arg; + } + $flag = NULL; + continue; + } + + if (isset($flag)) { + $params[$flag] = TRUE; + $flag = NULL; + } + + if ($opt !== '') { + $pair = explode('=', $opt, 2); + if (isset($pair[1])) { + $params[$pair[0]] = $pair[1]; + } else { + $flag = $pair[0]; + } + } + } + + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter & action in route definition.'); + } + $presenter = $params[self::PRESENTER_KEY]; + if ($a = strrpos($presenter, ':')) { + $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); + $presenter = substr($presenter, 0, $a); + } + + return new Application\Request( + $presenter, + 'CLI', + $params + ); + } + + + + /** + * This router is only unidirectional. + * @param Nette\Application\Request + * @param Nette\Http\Url + * @return NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + return NULL; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + return $this->defaults; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Routers/Route.php b/apigen/libs/Nette/Nette/Application/Routers/Route.php new file mode 100644 index 00000000000..1c5c5816459 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Routers/Route.php @@ -0,0 +1,825 @@ + array( // default style for path parameters + self::PATTERN => '[^/]+', + self::FILTER_IN => 'rawurldecode', + self::FILTER_OUT => array(__CLASS__, 'param2path'), + ), + '?#' => array( // default style for query parameters + ), + 'module' => array( + self::PATTERN => '[a-z][a-z0-9.-]*', + self::FILTER_IN => array(__CLASS__, 'path2presenter'), + self::FILTER_OUT => array(__CLASS__, 'presenter2path'), + ), + 'presenter' => array( + self::PATTERN => '[a-z][a-z0-9.-]*', + self::FILTER_IN => array(__CLASS__, 'path2presenter'), + self::FILTER_OUT => array(__CLASS__, 'presenter2path'), + ), + 'action' => array( + self::PATTERN => '[a-z][a-z0-9-]*', + self::FILTER_IN => array(__CLASS__, 'path2action'), + self::FILTER_OUT => array(__CLASS__, 'action2path'), + ), + '?module' => array( + ), + '?presenter' => array( + ), + '?action' => array( + ), + ); + + /** @var string */ + private $mask; + + /** @var array */ + private $sequence; + + /** @var string regular expression pattern */ + private $re; + + /** @var array of [value & fixity, filterIn, filterOut] */ + private $metadata = array(); + + /** @var array */ + private $xlat; + + /** @var int HOST, PATH, RELATIVE */ + private $type; + + /** @var int */ + private $flags; + + + + /** + * @param string URL mask, e.g. '//' + * @param array|string default values or metadata + * @param int flags + */ + public function __construct($mask, $metadata = array(), $flags = 0) + { + if (is_string($metadata)) { + $a = strrpos($metadata, ':'); + if (!$a) { + throw new Nette\InvalidArgumentException("Second argument must be array or string in format Presenter:action, '$metadata' given."); + } + $metadata = array( + self::PRESENTER_KEY => substr($metadata, 0, $a), + 'action' => $a === strlen($metadata) - 1 ? NULL : substr($metadata, $a + 1), + ); + } elseif ($metadata instanceof \Closure || $metadata instanceof Nette\Callback) { + $metadata = array( + self::PRESENTER_KEY => 'Nette:Micro', + 'callback' => $metadata, + ); + } + + $this->flags = $flags | static::$defaultFlags; + $this->setMask($mask, $metadata); + } + + + + /** + * Maps HTTP request to a Request object. + * @param Nette\Http\IRequest + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + // combine with precedence: mask (params in URL-path), fixity, query, (post,) defaults + + // 1) URL MASK + $url = $httpRequest->getUrl(); + + if ($this->type === self::HOST) { + $path = '//' . $url->getHost() . $url->getPath(); + + } elseif ($this->type === self::RELATIVE) { + $basePath = $url->getBasePath(); + if (strncmp($url->getPath(), $basePath, strlen($basePath)) !== 0) { + return NULL; + } + $path = (string) substr($url->getPath(), strlen($basePath)); + + } else { + $path = $url->getPath(); + } + + if ($path !== '') { + $path = rtrim($path, '/') . '/'; + } + + if (!$matches = Strings::match($path, $this->re)) { + // stop, not matched + return NULL; + } + + // deletes numeric keys, restore '-' chars + $params = array(); + foreach ($matches as $k => $v) { + if (is_string($k) && $v !== '') { + $params[str_replace('___', '-', $k)] = $v; // trick + } + } + + + // 2) CONSTANT FIXITY + foreach ($this->metadata as $name => $meta) { + if (isset($params[$name])) { + //$params[$name] = $this->flags & self::CASE_SENSITIVE === 0 ? strtolower($params[$name]) : */$params[$name]; // strtolower damages UTF-8 + + } elseif (isset($meta['fixity']) && $meta['fixity'] !== self::OPTIONAL) { + $params[$name] = NULL; // cannot be overwriten in 3) and detected by isset() in 4) + } + } + + + // 3) QUERY + if ($this->xlat) { + $params += self::renameKeys($httpRequest->getQuery(), array_flip($this->xlat)); + } else { + $params += $httpRequest->getQuery(); + } + + + // 4) APPLY FILTERS & FIXITY + foreach ($this->metadata as $name => $meta) { + if (isset($params[$name])) { + if (!is_scalar($params[$name])) { + + } elseif (isset($meta[self::FILTER_TABLE][$params[$name]])) { // applies filterTable only to scalar parameters + $params[$name] = $meta[self::FILTER_TABLE][$params[$name]]; + + } elseif (isset($meta[self::FILTER_TABLE]) && !empty($meta[self::FILTER_STRICT])) { + return NULL; // rejected by filterTable + + } elseif (isset($meta[self::FILTER_IN])) { // applies filterIn only to scalar parameters + $params[$name] = call_user_func($meta[self::FILTER_IN], (string) $params[$name]); + if ($params[$name] === NULL && !isset($meta['fixity'])) { + return NULL; // rejected by filter + } + } + + } elseif (isset($meta['fixity'])) { + $params[$name] = $meta[self::VALUE]; + } + } + + + // 5) BUILD Request + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter in route definition.'); + } + if (isset($this->metadata[self::MODULE_KEY])) { + if (!isset($params[self::MODULE_KEY])) { + throw new Nette\InvalidStateException('Missing module in route definition.'); + } + $presenter = $params[self::MODULE_KEY] . ':' . $params[self::PRESENTER_KEY]; + unset($params[self::MODULE_KEY], $params[self::PRESENTER_KEY]); + + } else { + $presenter = $params[self::PRESENTER_KEY]; + unset($params[self::PRESENTER_KEY]); + } + + return new Application\Request( + $presenter, + $httpRequest->getMethod(), + $params, + $httpRequest->getPost(), + $httpRequest->getFiles(), + array(Application\Request::SECURED => $httpRequest->isSecured()) + ); + } + + + + /** + * Constructs absolute URL from Request object. + * @param Nette\Application\Request + * @param Nette\Http\Url + * @return string|NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->flags & self::ONE_WAY) { + return NULL; + } + + $params = $appRequest->getParameters(); + $metadata = $this->metadata; + + $presenter = $appRequest->getPresenterName(); + $params[self::PRESENTER_KEY] = $presenter; + + if (isset($metadata[self::MODULE_KEY])) { // try split into module and [submodule:]presenter parts + $module = $metadata[self::MODULE_KEY]; + if (isset($module['fixity']) && strncasecmp($presenter, $module[self::VALUE] . ':', strlen($module[self::VALUE]) + 1) === 0) { + $a = strlen($module[self::VALUE]); + } else { + $a = strrpos($presenter, ':'); + } + if ($a === FALSE) { + $params[self::MODULE_KEY] = ''; + } else { + $params[self::MODULE_KEY] = substr($presenter, 0, $a); + $params[self::PRESENTER_KEY] = substr($presenter, $a + 1); + } + } + + foreach ($metadata as $name => $meta) { + if (!isset($params[$name])) { + continue; // retains NULL values + } + + if (isset($meta['fixity'])) { + if ($params[$name] === FALSE) { + $params[$name] = '0'; + } + if (is_scalar($params[$name]) ? strcasecmp($params[$name], $meta[self::VALUE]) === 0 + : $params[$name] === $meta[self::VALUE] + ) { // remove default values; NULL values are retain + unset($params[$name]); + continue; + + } elseif ($meta['fixity'] === self::CONSTANT) { + return NULL; // missing or wrong parameter '$name' + } + } + + if (!is_scalar($params[$name])) { + + } elseif (isset($meta['filterTable2'][$params[$name]])) { + $params[$name] = $meta['filterTable2'][$params[$name]]; + + } elseif (isset($meta['filterTable2']) && !empty($meta[self::FILTER_STRICT])) { + return NULL; + + } elseif (isset($meta[self::FILTER_OUT])) { + $params[$name] = call_user_func($meta[self::FILTER_OUT], $params[$name]); + } + + if (isset($meta[self::PATTERN]) && !preg_match($meta[self::PATTERN], rawurldecode($params[$name]))) { + return NULL; // pattern not match + } + } + + // compositing path + $sequence = $this->sequence; + $brackets = array(); + $required = NULL; // NULL for auto-optional + $url = ''; + $i = count($sequence) - 1; + do { + $url = $sequence[$i] . $url; + if ($i === 0) { + break; + } + $i--; + + $name = $sequence[$i]; $i--; // parameter name + + if ($name === ']') { // opening optional part + $brackets[] = $url; + + } elseif ($name[0] === '[') { // closing optional part + $tmp = array_pop($brackets); + if ($required < count($brackets) + 1) { // is this level optional? + if ($name !== '[!') { // and not "required"-optional + $url = $tmp; + } + } else { + $required = count($brackets); + } + + } elseif ($name[0] === '?') { // "foo" parameter + continue; + + } elseif (isset($params[$name]) && $params[$name] != '') { // intentionally == + $required = count($brackets); // make this level required + $url = $params[$name] . $url; + unset($params[$name]); + + } elseif (isset($metadata[$name]['fixity'])) { // has default value? + if ($required === NULL && !$brackets) { // auto-optional + $url = ''; + } else { + $url = $metadata[$name]['defOut'] . $url; + } + + } else { + return NULL; // missing parameter '$name' + } + } while (TRUE); + + + // build query string + if ($this->xlat) { + $params = self::renameKeys($params, $this->xlat); + } + + $sep = ini_get('arg_separator.input'); + $query = http_build_query($params, '', $sep ? $sep[0] : '&'); + if ($query != '') { // intentionally == + $url .= '?' . $query; + } + + // absolutize path + if ($this->type === self::RELATIVE) { + $url = '//' . $refUrl->getAuthority() . $refUrl->getBasePath() . $url; + + } elseif ($this->type === self::PATH) { + $url = '//' . $refUrl->getAuthority() . $url; + } + + if (strpos($url, '//', 2) !== FALSE) { + return NULL; // TODO: implement counterpart in match() ? + } + + $url = ($this->flags & self::SECURED ? 'https:' : 'http:') . $url; + + return $url; + } + + + + /** + * Parse mask and array of default values; initializes object. + * @param string + * @param array + * @return void + */ + private function setMask($mask, array $metadata) + { + $this->mask = $mask; + + // detect '//host/path' vs. '/abs. path' vs. 'relative path' + if (substr($mask, 0, 2) === '//') { + $this->type = self::HOST; + + } elseif (substr($mask, 0, 1) === '/') { + $this->type = self::PATH; + + } else { + $this->type = self::RELATIVE; + } + + foreach ($metadata as $name => $meta) { + if (!is_array($meta)) { + $metadata[$name] = array(self::VALUE => $meta, 'fixity' => self::CONSTANT); + + } elseif (array_key_exists(self::VALUE, $meta)) { + $metadata[$name]['fixity'] = self::CONSTANT; + } + } + + // PARSE MASK + // or [ or ] or ?... + $parts = Strings::split($mask, '/<([^>#= ]+)(=[^># ]*)? *([^>#]*)(#?[^>\[\]]*)>|(\[!?|\]|\s*\?.*)/'); + + $this->xlat = array(); + $i = count($parts) - 1; + + // PARSE QUERY PART OF MASK + if (isset($parts[$i - 1]) && substr(ltrim($parts[$i - 1]), 0, 1) === '?') { + // name= + $matches = Strings::matchAll($parts[$i - 1], '/(?:([a-zA-Z0-9_.-]+)=)?<([^># ]+) *([^>#]*)(#?[^>]*)>/'); + + foreach ($matches as $match) { + list(, $param, $name, $pattern, $class) = $match; // $pattern is not used + + if ($class !== '') { + if (!isset(static::$styles[$class])) { + throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); + } + $meta = static::$styles[$class]; + + } elseif (isset(static::$styles['?' . $name])) { + $meta = static::$styles['?' . $name]; + + } else { + $meta = static::$styles['?#']; + } + + if (isset($metadata[$name])) { + $meta = $metadata[$name] + $meta; + } + + if (array_key_exists(self::VALUE, $meta)) { + $meta['fixity'] = self::OPTIONAL; + } + + unset($meta['pattern']); + $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); + + $metadata[$name] = $meta; + if ($param !== '') { + $this->xlat[$name] = $param; + } + } + $i -= 6; + } + + // PARSE PATH PART OF MASK + $brackets = 0; // optional level + $re = ''; + $sequence = array(); + $autoOptional = TRUE; + do { + array_unshift($sequence, $parts[$i]); + $re = preg_quote($parts[$i], '#') . $re; + if ($i === 0) { + break; + } + $i--; + + $part = $parts[$i]; // [ or ] + if ($part === '[' || $part === ']' || $part === '[!') { + $brackets += $part[0] === '[' ? -1 : 1; + if ($brackets < 0) { + throw new Nette\InvalidArgumentException("Unexpected '$part' in mask '$mask'."); + } + array_unshift($sequence, $part); + $re = ($part[0] === '[' ? '(?:' : ')?') . $re; + $i -= 5; + continue; + } + + $class = $parts[$i]; $i--; // validation class + $pattern = trim($parts[$i]); $i--; // validation condition (as regexp) + $default = $parts[$i]; $i--; // default value + $name = $parts[$i]; $i--; // parameter name + array_unshift($sequence, $name); + + if ($name[0] === '?') { // "foo" parameter + $re = '(?:' . preg_quote(substr($name, 1), '#') . '|' . $pattern . ')' . $re; + $sequence[1] = substr($name, 1) . $sequence[1]; + continue; + } + + // check name (limitation by regexp) + if (preg_match('#[^a-z0-9_-]#i', $name)) { + throw new Nette\InvalidArgumentException("Parameter name must be alphanumeric string due to limitations of PCRE, '$name' given."); + } + + // pattern, condition & metadata + if ($class !== '') { + if (!isset(static::$styles[$class])) { + throw new Nette\InvalidStateException("Parameter '$name' has '$class' flag, but Route::\$styles['$class'] is not set."); + } + $meta = static::$styles[$class]; + + } elseif (isset(static::$styles[$name])) { + $meta = static::$styles[$name]; + + } else { + $meta = static::$styles['#']; + } + + if (isset($metadata[$name])) { + $meta = $metadata[$name] + $meta; + } + + if ($pattern == '' && isset($meta[self::PATTERN])) { + $pattern = $meta[self::PATTERN]; + } + + if ($default !== '') { + $meta[self::VALUE] = (string) substr($default, 1); + $meta['fixity'] = self::PATH_OPTIONAL; + } + + $meta['filterTable2'] = empty($meta[self::FILTER_TABLE]) ? NULL : array_flip($meta[self::FILTER_TABLE]); + if (array_key_exists(self::VALUE, $meta)) { + if (isset($meta['filterTable2'][$meta[self::VALUE]])) { + $meta['defOut'] = $meta['filterTable2'][$meta[self::VALUE]]; + + } elseif (isset($meta[self::FILTER_OUT])) { + $meta['defOut'] = call_user_func($meta[self::FILTER_OUT], $meta[self::VALUE]); + + } else { + $meta['defOut'] = $meta[self::VALUE]; + } + } + $meta[self::PATTERN] = "#(?:$pattern)$#A" . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); + + // include in expression + $re = '(?P<' . str_replace('-', '___', $name) . '>(?U)' . $pattern . ')' . $re; // str_replace is dirty trick to enable '-' in parameter name + if ($brackets) { // is in brackets? + if (!isset($meta[self::VALUE])) { + $meta[self::VALUE] = $meta['defOut'] = NULL; + } + $meta['fixity'] = self::PATH_OPTIONAL; + + } elseif (!$autoOptional) { + unset($meta['fixity']); + + } elseif (isset($meta['fixity'])) { // auto-optional + $re = '(?:' . $re . ')?'; + $meta['fixity'] = self::PATH_OPTIONAL; + + } else { + $autoOptional = FALSE; + } + + $metadata[$name] = $meta; + } while (TRUE); + + if ($brackets) { + throw new Nette\InvalidArgumentException("Missing closing ']' in mask '$mask'."); + } + + $this->re = '#' . $re . '/?$#A' . ($this->flags & self::CASE_SENSITIVE ? '' : 'iu'); + $this->metadata = $metadata; + $this->sequence = $sequence; + } + + + + /** + * Returns mask. + * @return string + */ + public function getMask() + { + return $this->mask; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + $defaults = array(); + foreach ($this->metadata as $name => $meta) { + if (isset($meta['fixity'])) { + $defaults[$name] = $meta[self::VALUE]; + } + } + return $defaults; + } + + + + /** + * Returns flags. + * @return int + */ + public function getFlags() + { + return $this->flags; + } + + + + /********************* Utilities ****************d*g**/ + + + + /** + * Proprietary cache aim. + * @return string|FALSE + */ + public function getTargetPresenter() + { + if ($this->flags & self::ONE_WAY) { + return FALSE; + } + + $m = $this->metadata; + $module = ''; + + if (isset($m[self::MODULE_KEY])) { + if (isset($m[self::MODULE_KEY]['fixity']) && $m[self::MODULE_KEY]['fixity'] === self::CONSTANT) { + $module = $m[self::MODULE_KEY][self::VALUE] . ':'; + } else { + return NULL; + } + } + + if (isset($m[self::PRESENTER_KEY]['fixity']) && $m[self::PRESENTER_KEY]['fixity'] === self::CONSTANT) { + return $module . $m[self::PRESENTER_KEY][self::VALUE]; + } + return NULL; + } + + + + /** + * Rename keys in array. + * @param array + * @param array + * @return array + */ + private static function renameKeys($arr, $xlat) + { + if (empty($xlat)) { + return $arr; + } + + $res = array(); + $occupied = array_flip($xlat); + foreach ($arr as $k => $v) { + if (isset($xlat[$k])) { + $res[$xlat[$k]] = $v; + + } elseif (!isset($occupied[$k])) { + $res[$k] = $v; + } + } + return $res; + } + + + + /********************* Inflectors ****************d*g**/ + + + + /** + * camelCaseAction name -> dash-separated. + * @param string + * @return string + */ + private static function action2path($s) + { + $s = preg_replace('#(.)(?=[A-Z])#', '$1-', $s); + $s = strtolower($s); + $s = rawurlencode($s); + return $s; + } + + + + /** + * dash-separated -> camelCaseAction name. + * @param string + * @return string + */ + private static function path2action($s) + { + $s = strtolower($s); + $s = preg_replace('#-(?=[a-z])#', ' ', $s); + $s = substr(ucwords('x' . $s), 1); + //$s = lcfirst(ucwords($s)); + $s = str_replace(' ', '', $s); + return $s; + } + + + + /** + * PascalCase:Presenter name -> dash-and-dot-separated. + * @param string + * @return string + */ + private static function presenter2path($s) + { + $s = strtr($s, ':', '.'); + $s = preg_replace('#([^.])(?=[A-Z])#', '$1-', $s); + $s = strtolower($s); + $s = rawurlencode($s); + return $s; + } + + + + /** + * dash-and-dot-separated -> PascalCase:Presenter name. + * @param string + * @return string + */ + private static function path2presenter($s) + { + $s = strtolower($s); + $s = preg_replace('#([.-])(?=[a-z])#', '$1 ', $s); + $s = ucwords($s); + $s = str_replace('. ', ':', $s); + $s = str_replace('- ', '', $s); + return $s; + } + + + + /** + * Url encode. + * @param string + * @return string + */ + private static function param2path($s) + { + return str_replace('%2F', '/', rawurlencode($s)); + } + + + + /********************* Route::$styles manipulator ****************d*g**/ + + + + /** + * Creates new style. + * @param string style name (#style, urlParameter, ?queryParameter) + * @param string optional parent style name + * @return void + */ + public static function addStyle($style, $parent = '#') + { + if (isset(static::$styles[$style])) { + throw new Nette\InvalidArgumentException("Style '$style' already exists."); + } + + if ($parent !== NULL) { + if (!isset(static::$styles[$parent])) { + throw new Nette\InvalidArgumentException("Parent style '$parent' doesn't exist."); + } + static::$styles[$style] = static::$styles[$parent]; + + } else { + static::$styles[$style] = array(); + } + } + + + + /** + * Changes style property value. + * @param string style name (#style, urlParameter, ?queryParameter) + * @param string property name (Route::PATTERN, Route::FILTER_IN, Route::FILTER_OUT, Route::FILTER_TABLE) + * @param mixed property value + * @return void + */ + public static function setStyleProperty($style, $key, $value) + { + if (!isset(static::$styles[$style])) { + throw new Nette\InvalidArgumentException("Style '$style' doesn't exist."); + } + static::$styles[$style][$key] = $value; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Routers/RouteList.php b/apigen/libs/Nette/Nette/Application/Routers/RouteList.php new file mode 100644 index 00000000000..05dffb5d112 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Routers/RouteList.php @@ -0,0 +1,146 @@ +module = $module ? $module . ':' : ''; + } + + + + /** + * Maps HTTP request to a Request object. + * @param Nette\Http\IRequest + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + foreach ($this as $route) { + $appRequest = $route->match($httpRequest); + if ($appRequest !== NULL) { + $appRequest->setPresenterName($this->module . $appRequest->getPresenterName()); + return $appRequest; + } + } + return NULL; + } + + + + /** + * Constructs absolute URL from Request object. + * @param Nette\Application\Request + * @param Nette\Http\Url + * @return string|NULL + */ + public function constructUrl(Nette\Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->cachedRoutes === NULL) { + $routes = array(); + $routes['*'] = array(); + + foreach ($this as $route) { + $presenter = $route instanceof Route ? $route->getTargetPresenter() : NULL; + + if ($presenter === FALSE) { + continue; + } + + if (is_string($presenter)) { + $presenter = strtolower($presenter); + if (!isset($routes[$presenter])) { + $routes[$presenter] = $routes['*']; + } + $routes[$presenter][] = $route; + + } else { + foreach ($routes as $id => $foo) { + $routes[$id][] = $route; + } + } + } + + $this->cachedRoutes = $routes; + } + + if ($this->module) { + if (strncasecmp($tmp = $appRequest->getPresenterName(), $this->module, strlen($this->module)) === 0) { + $appRequest = clone $appRequest; + $appRequest->setPresenterName(substr($tmp, strlen($this->module))); + } else { + return NULL; + } + } + + $presenter = strtolower($appRequest->getPresenterName()); + if (!isset($this->cachedRoutes[$presenter])) { + $presenter = '*'; + } + + foreach ($this->cachedRoutes[$presenter] as $route) { + $url = $route->constructUrl($appRequest, $refUrl); + if ($url !== NULL) { + return $url; + } + } + + return NULL; + } + + + + /** + * Adds the router. + * @param mixed + * @param Nette\Application\IRouter + * @return void + */ + public function offsetSet($index, $route) + { + if (!$route instanceof Nette\Application\IRouter) { + throw new Nette\InvalidArgumentException("Argument must be IRouter descendant."); + } + parent::offsetSet($index, $route); + } + + + + /** + * @return string + */ + public function getModule() + { + return $this->module; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/Routers/SimpleRouter.php b/apigen/libs/Nette/Nette/Application/Routers/SimpleRouter.php new file mode 100644 index 00000000000..acd1e7171ec --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/Routers/SimpleRouter.php @@ -0,0 +1,163 @@ + substr($defaults, 0, $a), + 'action' => $a === strlen($defaults) - 1 ? Application\UI\Presenter::DEFAULT_ACTION : substr($defaults, $a + 1), + ); + } + + if (isset($defaults[self::MODULE_KEY])) { + $this->module = $defaults[self::MODULE_KEY] . ':'; + unset($defaults[self::MODULE_KEY]); + } + + $this->defaults = $defaults; + $this->flags = $flags; + } + + + + /** + * Maps HTTP request to a Request object. + * @param Nette\Http\IRequest + * @return Nette\Application\Request|NULL + */ + public function match(Nette\Http\IRequest $httpRequest) + { + if ($httpRequest->getUrl()->getPathInfo() !== '') { + return NULL; + } + // combine with precedence: get, (post,) defaults + $params = $httpRequest->getQuery(); + $params += $this->defaults; + + if (!isset($params[self::PRESENTER_KEY])) { + throw new Nette\InvalidStateException('Missing presenter.'); + } + + $presenter = $this->module . $params[self::PRESENTER_KEY]; + unset($params[self::PRESENTER_KEY]); + + return new Application\Request( + $presenter, + $httpRequest->getMethod(), + $params, + $httpRequest->getPost(), + $httpRequest->getFiles(), + array(Application\Request::SECURED => $httpRequest->isSecured()) + ); + } + + + + /** + * Constructs absolute URL from Request object. + * @param Nette\Application\Request + * @param Nette\Http\Url + * @return string|NULL + */ + public function constructUrl(Application\Request $appRequest, Nette\Http\Url $refUrl) + { + if ($this->flags & self::ONE_WAY) { + return NULL; + } + $params = $appRequest->getParameters(); + + // presenter name + $presenter = $appRequest->getPresenterName(); + if (strncasecmp($presenter, $this->module, strlen($this->module)) === 0) { + $params[self::PRESENTER_KEY] = substr($presenter, strlen($this->module)); + } else { + return NULL; + } + + // remove default values; NULL values are retain + foreach ($this->defaults as $key => $value) { + if (isset($params[$key]) && $params[$key] == $value) { // intentionally == + unset($params[$key]); + } + } + + $url = ($this->flags & self::SECURED ? 'https://' : 'http://') . $refUrl->getAuthority() . $refUrl->getPath(); + $sep = ini_get('arg_separator.input'); + $query = http_build_query($params, '', $sep ? $sep[0] : '&'); + if ($query != '') { // intentionally == + $url .= '?' . $query; + } + return $url; + } + + + + /** + * Returns default values. + * @return array + */ + public function getDefaults() + { + return $this->defaults; + } + + + + /** + * Returns flags. + * @return int + */ + public function getFlags() + { + return $this->flags; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/BadSignalException.php b/apigen/libs/Nette/Nette/Application/UI/BadSignalException.php new file mode 100644 index 00000000000..4401bb5c5a4 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/BadSignalException.php @@ -0,0 +1,28 @@ +template === NULL) { + $value = $this->createTemplate(); + if (!$value instanceof Nette\Templating\ITemplate && $value !== NULL) { + $class2 = get_class($value); $class = get_class($this); + throw new Nette\UnexpectedValueException("Object returned by $class::createTemplate() must be instance of Nette\\Templating\\ITemplate, '$class2' given."); + } + $this->template = $value; + } + return $this->template; + } + + + + /** + * @param string|NULL + * @return Nette\Templating\ITemplate + */ + protected function createTemplate($class = NULL) + { + $template = $class ? new $class : new Nette\Templating\FileTemplate; + $presenter = $this->getPresenter(FALSE); + $template->onPrepareFilters[] = $this->templatePrepareFilters; + $template->registerHelperLoader('Nette\Templating\Helpers::loader'); + + // default parameters + $template->control = $template->_control = $this; + $template->presenter = $template->_presenter = $presenter; + if ($presenter instanceof Presenter) { + $template->setCacheStorage($presenter->getContext()->nette->templateCacheStorage); + $template->user = $presenter->getUser(); + $template->netteHttpResponse = $presenter->getHttpResponse(); + $template->netteCacheStorage = $presenter->getContext()->getByType('Nette\Caching\IStorage'); + $template->baseUri = $template->baseUrl = rtrim($presenter->getHttpRequest()->getUrl()->getBaseUrl(), '/'); + $template->basePath = preg_replace('#https?://[^/]+#A', '', $template->baseUrl); + + // flash message + if ($presenter->hasFlashSession()) { + $id = $this->getParameterId('flash'); + $template->flashes = $presenter->getFlashSession()->$id; + } + } + if (!isset($template->flashes) || !is_array($template->flashes)) { + $template->flashes = array(); + } + + return $template; + } + + + + /** + * Descendant can override this method to customize template compile-time filters. + * @param Nette\Templating\Template + * @return void + */ + public function templatePrepareFilters($template) + { + $template->registerFilter($this->getPresenter()->getContext()->nette->createLatte()); + } + + + + /** + * Returns widget component specified by name. + * @param string + * @return Nette\ComponentModel\IComponent + */ + public function getWidget($name) + { + trigger_error(__METHOD__ . '() is deprecated, use getComponent() instead.', E_USER_WARNING); + return $this->getComponent($name); + } + + + + /** + * Saves the message to template, that can be displayed after redirect. + * @param string + * @param string + * @return \stdClass + */ + public function flashMessage($message, $type = 'info') + { + $id = $this->getParameterId('flash'); + $messages = $this->getPresenter()->getFlashSession()->$id; + $messages[] = $flash = (object) array( + 'message' => $message, + 'type' => $type, + ); + $this->getTemplate()->flashes = $messages; + $this->getPresenter()->getFlashSession()->$id = $messages; + return $flash; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Forces control or its snippet to repaint. + * @param string + * @return void + */ + public function invalidateControl($snippet = NULL) + { + $this->invalidSnippets[$snippet] = TRUE; + } + + + + /** + * Allows control or its snippet to not repaint. + * @param string + * @return void + */ + public function validateControl($snippet = NULL) + { + if ($snippet === NULL) { + $this->invalidSnippets = array(); + + } else { + unset($this->invalidSnippets[$snippet]); + } + } + + + + /** + * Is required to repaint the control or its snippet? + * @param string snippet name + * @return bool + */ + public function isControlInvalid($snippet = NULL) + { + if ($snippet === NULL) { + if (count($this->invalidSnippets) > 0) { + return TRUE; + + } else { + $queue = array($this); + do { + foreach (array_shift($queue)->getComponents() as $component) { + if ($component instanceof IRenderable) { + if ($component->isControlInvalid()) { + // $this->invalidSnippets['__child'] = TRUE; // as cache + return TRUE; + } + + } elseif ($component instanceof Nette\ComponentModel\IContainer) { + $queue[] = $component; + } + } + } while ($queue); + + return FALSE; + } + + } else { + return isset($this->invalidSnippets[NULL]) || isset($this->invalidSnippets[$snippet]); + } + } + + + + /** + * Returns snippet HTML ID. + * @param string snippet name + * @return string + */ + public function getSnippetId($name = NULL) + { + // HTML 4 ID & NAME: [A-Za-z][A-Za-z0-9:_.-]* + return 'snippet-' . $this->getUniqueId() . '-' . $name; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/Form.php b/apigen/libs/Nette/Nette/Application/UI/Form.php new file mode 100644 index 00000000000..6db389a116b --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/Form.php @@ -0,0 +1,147 @@ +monitor('Nette\Application\UI\Presenter'); + if ($parent !== NULL) { + $parent->addComponent($this, $name); + } + } + + + + /** + * Returns the presenter where this component belongs to. + * @param bool throw exception if presenter doesn't exist? + * @return Presenter|NULL + */ + public function getPresenter($need = TRUE) + { + return $this->lookup('Nette\Application\UI\Presenter', $need); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\ComponentModel\IComponent + * @return void + */ + protected function attached($presenter) + { + if ($presenter instanceof Presenter) { + $name = $this->lookupPath('Nette\Application\UI\Presenter'); + + if (!isset($this->getElementPrototype()->id)) { + $this->getElementPrototype()->id = 'frm-' . $name; + } + + $this->setAction(new Link( + $presenter, + $name . self::NAME_SEPARATOR . 'submit!', + array() + )); + + // fill-in the form with HTTP data + if ($this->isSubmitted()) { + foreach ($this->getControls() as $control) { + if (!$control->isDisabled()) { + $control->loadHttpData(); + } + } + } + } + parent::attached($presenter); + } + + + + /** + * Tells if the form is anchored. + * @return bool + */ + public function isAnchored() + { + return (bool) $this->getPresenter(FALSE); + } + + + + /** + * Internal: receives submitted HTTP data. + * @return array + */ + protected function receiveHttpData() + { + $presenter = $this->getPresenter(); + if (!$presenter->isSignalReceiver($this, 'submit')) { + return; + } + + $isPost = $this->getMethod() === self::POST; + $request = $presenter->getRequest(); + if ($request->isMethod('forward') || $request->isMethod('post') !== $isPost) { + return; + } + + if ($isPost) { + return Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles()); + } else { + return $request->getParameters(); + } + } + + + + /********************* interface ISignalReceiver ****************d*g**/ + + + + /** + * This method is called by presenter. + * @param string + * @return void + */ + public function signalReceived($signal) + { + if ($signal === 'submit') { + if (!$this->getPresenter()->getRequest()->hasFlag(Nette\Application\Request::RESTORED)) { + $this->fireEvents(); + } + } else { + $class = get_class($this); + throw new BadSignalException("Missing handler for signal '$signal' in $class."); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/IRenderable.php b/apigen/libs/Nette/Nette/Application/UI/IRenderable.php new file mode 100644 index 00000000000..1a96448aebe --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/IRenderable.php @@ -0,0 +1,38 @@ +previous = $previous; + parent::__construct($message, $code); + } else { + parent::__construct($message, $code, $previous); + } + } + */ +} diff --git a/apigen/libs/Nette/Nette/Application/UI/Link.php b/apigen/libs/Nette/Nette/Application/UI/Link.php new file mode 100644 index 00000000000..4da459759ab --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/Link.php @@ -0,0 +1,117 @@ +component = $component; + $this->destination = $destination; + $this->params = $params; + } + + + + /** + * Returns link destination. + * @return string + */ + public function getDestination() + { + return $this->destination; + } + + + + /** + * Changes link parameter. + * @param string + * @param mixed + * @return Link provides a fluent interface + */ + public function setParameter($key, $value) + { + $this->params[$key] = $value; + return $this; + } + + + + /** + * Returns link parameter. + * @param string + * @return mixed + */ + public function getParameter($key) + { + return isset($this->params[$key]) ? $this->params[$key] : NULL; + } + + + + /** + * Returns link parameters. + * @return array + */ + public function getParameters() + { + return $this->params; + } + + + + /** + * Converts link to URL. + * @return string + */ + public function __toString() + { + try { + return $this->component->link($this->destination, $this->params); + + } catch (\Exception $e) { + Nette\Diagnostics\Debugger::toStringException($e); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/Multiplier.php b/apigen/libs/Nette/Nette/Application/UI/Multiplier.php new file mode 100644 index 00000000000..f9d8bd323b3 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/Multiplier.php @@ -0,0 +1,42 @@ +factory = new Nette\Callback($factory); + } + + + + protected function createComponent($name) + { + return $this->factory->invoke($name, $this); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/Presenter.php b/apigen/libs/Nette/Nette/Application/UI/Presenter.php new file mode 100644 index 00000000000..04ebc4da9c7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/Presenter.php @@ -0,0 +1,1446 @@ +context = $context; + if ($context && $this->invalidLinkMode === NULL) { + $this->invalidLinkMode = $context->parameters['productionMode'] ? self::INVALID_LINK_SILENT : self::INVALID_LINK_WARNING; + } + } + + + + /** + * @return Nette\Application\Request + */ + final public function getRequest() + { + return $this->request; + } + + + + /** + * Returns self. + * @return Presenter + */ + final public function getPresenter($need = TRUE) + { + return $this; + } + + + + /** + * Returns a name that uniquely identifies component. + * @return string + */ + final public function getUniqueId() + { + return ''; + } + + + + /********************* interface IPresenter ****************d*g**/ + + + + /** + * @param Nette\Application\Request + * @return Nette\Application\IResponse + */ + public function run(Application\Request $request) + { + try { + // STARTUP + $this->request = $request; + $this->payload = (object) NULL; + $this->setParent($this->getParent(), $request->getPresenterName()); + + $this->initGlobalParameters(); + $this->checkRequirements($this->getReflection()); + $this->startup(); + if (!$this->startupCheck) { + $class = $this->getReflection()->getMethod('startup')->getDeclaringClass()->getName(); + throw new Nette\InvalidStateException("Method $class::startup() or its descendant doesn't call parent::startup()."); + } + // calls $this->action() + $this->tryCall($this->formatActionMethod($this->getAction()), $this->params); + + if ($this->autoCanonicalize) { + $this->canonicalize(); + } + if ($this->getHttpRequest()->isMethod('head')) { + $this->terminate(); + } + + // SIGNAL HANDLING + // calls $this->handle() + $this->processSignal(); + + // RENDERING VIEW + $this->beforeRender(); + // calls $this->render() + $this->tryCall($this->formatRenderMethod($this->getView()), $this->params); + $this->afterRender(); + + // save component tree persistent state + $this->saveGlobalState(); + if ($this->isAjax()) { + $this->payload->state = $this->getGlobalState(); + } + + // finish template rendering + $this->sendTemplate(); + + } catch (Application\AbortException $e) { + // continue with shutting down + if ($this->isAjax()) try { + $hasPayload = (array) $this->payload; unset($hasPayload['state']); + if ($this->response instanceof Responses\TextResponse && $this->isControlInvalid()) { // snippets - TODO + $this->snippetMode = TRUE; + $this->response->send($this->getHttpRequest(), $this->getHttpResponse()); + $this->sendPayload(); + + } elseif (!$this->response && $hasPayload) { // back compatibility for use terminate() instead of sendPayload() + $this->sendPayload(); + } + } catch (Application\AbortException $e) { } + + if ($this->hasFlashSession()) { + $this->getFlashSession()->setExpiration($this->response instanceof Responses\RedirectResponse ? '+ 30 seconds': '+ 3 seconds'); + } + + // SHUTDOWN + $this->onShutdown($this, $this->response); + $this->shutdown($this->response); + + return $this->response; + } + } + + + + /** + * @return void + */ + protected function startup() + { + $this->startupCheck = TRUE; + } + + + + /** + * Common render method. + * @return void + */ + protected function beforeRender() + { + } + + + + /** + * Common render method. + * @return void + */ + protected function afterRender() + { + } + + + + /** + * @param Nette\Application\IResponse optional catched exception + * @return void + */ + protected function shutdown($response) + { + } + + + + /** + * Checks authorization. + * @return void + */ + public function checkRequirements($element) + { + $user = (array) $element->getAnnotation('User'); + if (in_array('loggedIn', $user) && !$this->getUser()->isLoggedIn()) { + throw new Application\ForbiddenRequestException; + } + } + + + + /********************* signal handling ****************d*g**/ + + + + /** + * @return void + * @throws BadSignalException + */ + public function processSignal() + { + if ($this->signal === NULL) { + return; + } + + try { + $component = $this->signalReceiver === '' ? $this : $this->getComponent($this->signalReceiver, FALSE); + } catch (Nette\InvalidArgumentException $e) {} + + if (isset($e) || $component === NULL) { + throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not found."); + + } elseif (!$component instanceof ISignalReceiver) { + throw new BadSignalException("The signal receiver component '$this->signalReceiver' is not ISignalReceiver implementor."); + } + + $component->signalReceived($this->signal); + $this->signal = NULL; + } + + + + /** + * Returns pair signal receiver and name. + * @return array|NULL + */ + final public function getSignal() + { + return $this->signal === NULL ? NULL : array($this->signalReceiver, $this->signal); + } + + + + /** + * Checks if the signal receiver is the given one. + * @param mixed component or its id + * @param string signal name (optional) + * @return bool + */ + final public function isSignalReceiver($component, $signal = NULL) + { + if ($component instanceof Nette\ComponentModel\Component) { + $component = $component === $this ? '' : $component->lookupPath(__CLASS__, TRUE); + } + + if ($this->signal === NULL) { + return FALSE; + + } elseif ($signal === TRUE) { + return $component === '' + || strncmp($this->signalReceiver . '-', $component . '-', strlen($component) + 1) === 0; + + } elseif ($signal === NULL) { + return $this->signalReceiver === $component; + + } else { + return $this->signalReceiver === $component && strcasecmp($signal, $this->signal) === 0; + } + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Returns current action name. + * @return string + */ + final public function getAction($fullyQualified = FALSE) + { + return $fullyQualified ? ':' . $this->getName() . ':' . $this->action : $this->action; + } + + + + /** + * Changes current action. Only alphanumeric characters are allowed. + * @param string + * @return void + */ + public function changeAction($action) + { + if (is_string($action) && Nette\Utils\Strings::match($action, '#^[a-zA-Z0-9][a-zA-Z0-9_\x7f-\xff]*$#')) { + $this->action = $action; + $this->view = $action; + + } else { + $this->error('Action name is not alphanumeric string.'); + } + } + + + + /** + * Returns current view. + * @return string + */ + final public function getView() + { + return $this->view; + } + + + + /** + * Changes current view. Any name is allowed. + * @param string + * @return Presenter provides a fluent interface + */ + public function setView($view) + { + $this->view = (string) $view; + return $this; + } + + + + /** + * Returns current layout name. + * @return string|FALSE + */ + final public function getLayout() + { + return $this->layout; + } + + + + /** + * Changes or disables layout. + * @param string|FALSE + * @return Presenter provides a fluent interface + */ + public function setLayout($layout) + { + $this->layout = $layout === FALSE ? FALSE : (string) $layout; + return $this; + } + + + + /** + * @return void + * @throws Nette\Application\BadRequestException if no template found + * @throws Nette\Application\AbortException + */ + public function sendTemplate() + { + $template = $this->getTemplate(); + if (!$template) { + return; + } + + if ($template instanceof Nette\Templating\IFileTemplate && !$template->getFile()) { // content template + $files = $this->formatTemplateFiles(); + foreach ($files as $file) { + if (is_file($file)) { + $template->setFile($file); + break; + } + } + + if (!$template->getFile()) { + $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); + $file = strtr($file, '/', DIRECTORY_SEPARATOR); + $this->error("Page not found. Missing template '$file'."); + } + } + + $this->sendResponse(new Responses\TextResponse($template)); + } + + + + /** + * Finds layout template file name. + * @return string + */ + public function findLayoutTemplateFile() + { + if ($this->layout === FALSE) { + return; + } + $files = $this->formatLayoutTemplateFiles(); + foreach ($files as $file) { + if (is_file($file)) { + return $file; + } + } + + if ($this->layout) { + $file = preg_replace('#^.*([/\\\\].{1,70})$#U', "\xE2\x80\xA6\$1", reset($files)); + $file = strtr($file, '/', DIRECTORY_SEPARATOR); + throw new Nette\FileNotFoundException("Layout not found. Missing template '$file'."); + } + } + + + + /** + * Formats layout template file names. + * @return array + */ + public function formatLayoutTemplateFiles() + { + $name = $this->getName(); + $presenter = substr($name, strrpos(':' . $name, ':')); + $layout = $this->layout ? $this->layout : 'layout'; + $dir = dirname($this->getReflection()->getFileName()); + $dir = is_dir("$dir/templates") ? $dir : dirname($dir); + $list = array( + "$dir/templates/$presenter/@$layout.latte", + "$dir/templates/$presenter.@$layout.latte", + "$dir/templates/$presenter/@$layout.phtml", + "$dir/templates/$presenter.@$layout.phtml", + ); + do { + $list[] = "$dir/templates/@$layout.latte"; + $list[] = "$dir/templates/@$layout.phtml"; + $dir = dirname($dir); + } while ($dir && ($name = substr($name, 0, strrpos($name, ':')))); + return $list; + } + + + + /** + * Formats view template file names. + * @return array + */ + public function formatTemplateFiles() + { + $name = $this->getName(); + $presenter = substr($name, strrpos(':' . $name, ':')); + $dir = dirname($this->getReflection()->getFileName()); + $dir = is_dir("$dir/templates") ? $dir : dirname($dir); + return array( + "$dir/templates/$presenter/$this->view.latte", + "$dir/templates/$presenter.$this->view.latte", + "$dir/templates/$presenter/$this->view.phtml", + "$dir/templates/$presenter.$this->view.phtml", + ); + } + + + + /** + * Formats action method name. + * @param string + * @return string + */ + protected static function formatActionMethod($action) + { + return 'action' . $action; + } + + + + /** + * Formats render view method name. + * @param string + * @return string + */ + protected static function formatRenderMethod($view) + { + return 'render' . $view; + } + + + + /********************* partial AJAX rendering ****************d*g**/ + + + + /** + * @return \stdClass + */ + public function getPayload() + { + return $this->payload; + } + + + + /** + * Is AJAX request? + * @return bool + */ + public function isAjax() + { + if ($this->ajaxMode === NULL) { + $this->ajaxMode = $this->getHttpRequest()->isAjax(); + } + return $this->ajaxMode; + } + + + + /** + * Sends AJAX payload to the output. + * @return void + * @throws Nette\Application\AbortException + */ + public function sendPayload() + { + $this->sendResponse(new Responses\JsonResponse($this->payload)); + } + + + + /********************* navigation & flow ****************d*g**/ + + + + /** + * Sends response and terminates presenter. + * @param Nette\Application\IResponse + * @return void + * @throws Nette\Application\AbortException + */ + public function sendResponse(Application\IResponse $response) + { + $this->response = $response; + $this->terminate(); + } + + + + /** + * Correctly terminates presenter. + * @return void + * @throws Nette\Application\AbortException + */ + public function terminate() + { + if (func_num_args() !== 0) { + trigger_error(__METHOD__ . ' is not intended to send a Application\Response; use sendResponse() instead.', E_USER_WARNING); + $this->sendResponse(func_get_arg(0)); + } + throw new Application\AbortException(); + } + + + + /** + * Forward to another presenter or action. + * @param string|Request + * @param array|mixed + * @return void + * @throws Nette\Application\AbortException + */ + public function forward($destination, $args = array()) + { + if ($destination instanceof Application\Request) { + $this->sendResponse(new Responses\ForwardResponse($destination)); + + } elseif (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + $this->createRequest($this, $destination, $args, 'forward'); + $this->sendResponse(new Responses\ForwardResponse($this->lastCreatedRequest)); + } + + + + /** + * Redirect to another URL and ends presenter execution. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\AbortException + */ + public function redirectUrl($url, $code = NULL) + { + if ($this->isAjax()) { + $this->payload->redirect = (string) $url; + $this->sendPayload(); + + } elseif (!$code) { + $code = $this->getHttpRequest()->isMethod('post') + ? Http\IResponse::S303_POST_GET + : Http\IResponse::S302_FOUND; + } + $this->sendResponse(new Responses\RedirectResponse($url, $code)); + } + + /** @deprecated */ + function redirectUri($url, $code = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::redirectUrl() instead.', E_USER_WARNING); + $this->redirectUrl($url, $code); + } + + + + /** + * Throws HTTP error. + * @param string + * @param int HTTP error code + * @return void + * @throws Nette\Application\BadRequestException + */ + public function error($message = NULL, $code = Http\IResponse::S404_NOT_FOUND) + { + throw new Application\BadRequestException($message, $code); + } + + + + /** + * Link to myself. + * @return string + */ + public function backlink() + { + return $this->getAction(TRUE); + } + + + + /** + * Returns the last created Request. + * @return Nette\Application\Request + */ + public function getLastCreatedRequest() + { + return $this->lastCreatedRequest; + } + + + + /** + * Returns the last created Request flag. + * @param string + * @return bool + */ + public function getLastCreatedRequestFlag($flag) + { + return !empty($this->lastCreatedRequestFlag[$flag]); + } + + + + /** + * Conditional redirect to canonicalized URI. + * @return void + * @throws Nette\Application\AbortException + */ + public function canonicalize() + { + if (!$this->isAjax() && ($this->request->isMethod('get') || $this->request->isMethod('head'))) { + try { + $url = $this->createRequest($this, $this->action, $this->getGlobalState() + $this->request->getParameters(), 'redirectX'); + } catch (InvalidLinkException $e) {} + if (isset($url) && !$this->getHttpRequest()->getUrl()->isEqual($url)) { + $this->sendResponse(new Responses\RedirectResponse($url, Http\IResponse::S301_MOVED_PERMANENTLY)); + } + } + } + + + + /** + * Attempts to cache the sent entity by its last modification date. + * @param string|int|DateTime last modified time + * @param string strong entity tag validator + * @param mixed optional expiration time + * @return void + * @throws Nette\Application\AbortException + * @deprecated + */ + public function lastModified($lastModified, $etag = NULL, $expire = NULL) + { + if ($expire !== NULL) { + $this->getHttpResponse()->setExpiration($expire); + } + + if (!$this->getHttpContext()->isModified($lastModified, $etag)) { + $this->terminate(); + } + } + + + + /** + * Request/URL factory. + * @param PresenterComponent base + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array array of arguments + * @param string forward|redirect|link + * @return string URL + * @throws InvalidLinkException + * @internal + */ + final protected function createRequest($component, $destination, array $args, $mode) + { + // note: createRequest supposes that saveState(), run() & tryCall() behaviour is final + + // cached services for better performance + static $presenterFactory, $router, $refUrl; + if ($presenterFactory === NULL) { + $presenterFactory = $this->getApplication()->getPresenterFactory(); + $router = $this->getApplication()->getRouter(); + $refUrl = new Http\Url($this->getHttpRequest()->getUrl()); + $refUrl->setPath($this->getHttpRequest()->getUrl()->getScriptPath()); + } + + $this->lastCreatedRequest = $this->lastCreatedRequestFlag = NULL; + + // PARSE DESTINATION + // 1) fragment + $a = strpos($destination, '#'); + if ($a === FALSE) { + $fragment = ''; + } else { + $fragment = substr($destination, $a); + $destination = substr($destination, 0, $a); + } + + // 2) ?query syntax + $a = strpos($destination, '?'); + if ($a !== FALSE) { + parse_str(substr($destination, $a + 1), $args); // requires disabled magic quotes + $destination = substr($destination, 0, $a); + } + + // 3) URL scheme + $a = strpos($destination, '//'); + if ($a === FALSE) { + $scheme = FALSE; + } else { + $scheme = substr($destination, 0, $a); + $destination = substr($destination, $a + 2); + } + + // 4) signal or empty + if (!$component instanceof Presenter || substr($destination, -1) === '!') { + $signal = rtrim($destination, '!'); + $a = strrpos($signal, ':'); + if ($a !== FALSE) { + $component = $component->getComponent(strtr(substr($signal, 0, $a), ':', '-')); + $signal = (string) substr($signal, $a + 1); + } + if ($signal == NULL) { // intentionally == + throw new InvalidLinkException("Signal must be non-empty string."); + } + $destination = 'this'; + } + + if ($destination == NULL) { // intentionally == + throw new InvalidLinkException("Destination must be non-empty string."); + } + + // 5) presenter: action + $current = FALSE; + $a = strrpos($destination, ':'); + if ($a === FALSE) { + $action = $destination === 'this' ? $this->action : $destination; + $presenter = $this->getName(); + $presenterClass = get_class($this); + + } else { + $action = (string) substr($destination, $a + 1); + if ($destination[0] === ':') { // absolute + if ($a < 2) { + throw new InvalidLinkException("Missing presenter name in '$destination'."); + } + $presenter = substr($destination, 1, $a - 1); + + } else { // relative + $presenter = $this->getName(); + $b = strrpos($presenter, ':'); + if ($b === FALSE) { // no module + $presenter = substr($destination, 0, $a); + } else { // with module + $presenter = substr($presenter, 0, $b + 1) . substr($destination, 0, $a); + } + } + try { + $presenterClass = $presenterFactory->getPresenterClass($presenter); + } catch (Application\InvalidPresenterException $e) { + throw new InvalidLinkException($e->getMessage(), NULL, $e); + } + } + + // PROCESS SIGNAL ARGUMENTS + if (isset($signal)) { // $component must be IStatePersistent + $reflection = new PresenterComponentReflection(get_class($component)); + if ($signal === 'this') { // means "no signal" + $signal = ''; + if (array_key_exists(0, $args)) { + throw new InvalidLinkException("Unable to pass parameters to 'this!' signal."); + } + + } elseif (strpos($signal, self::NAME_SEPARATOR) === FALSE) { // TODO: AppForm exception + // counterpart of signalReceived() & tryCall() + $method = $component->formatSignalMethod($signal); + if (!$reflection->hasCallableMethod($method)) { + throw new InvalidLinkException("Unknown signal '$signal', missing handler {$reflection->name}::$method()"); + } + if ($args) { // convert indexed parameters to named + self::argsToParams(get_class($component), $method, $args); + } + } + + // counterpart of IStatePersistent + if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { + $component->saveState($args); + } + + if ($args && $component !== $this) { + $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; + foreach ($args as $key => $val) { + unset($args[$key]); + $args[$prefix . $key] = $val; + } + } + } + + // PROCESS ARGUMENTS + if (is_subclass_of($presenterClass, __CLASS__)) { + if ($action === '') { + $action = self::DEFAULT_ACTION; + } + + $current = ($action === '*' || strcasecmp($action, $this->action) === 0) && $presenterClass === get_class($this); // TODO + + $reflection = new PresenterComponentReflection($presenterClass); + if ($args || $destination === 'this') { + // counterpart of run() & tryCall() + /**/$method = $presenterClass::formatActionMethod($action);/**/ + /*5.2* $method = call_user_func(array($presenterClass, 'formatActionMethod'), $action);*/ + if (!$reflection->hasCallableMethod($method)) { + /**/$method = $presenterClass::formatRenderMethod($action);/**/ + /*5.2* $method = call_user_func(array($presenterClass, 'formatRenderMethod'), $action);*/ + if (!$reflection->hasCallableMethod($method)) { + $method = NULL; + } + } + + // convert indexed parameters to named + if ($method === NULL) { + if (array_key_exists(0, $args)) { + throw new InvalidLinkException("Unable to pass parameters to action '$presenter:$action', missing corresponding method."); + } + + } elseif ($destination === 'this') { + self::argsToParams($presenterClass, $method, $args, $this->params); + + } else { + self::argsToParams($presenterClass, $method, $args); + } + } + + // counterpart of IStatePersistent + if ($args && array_intersect_key($args, $reflection->getPersistentParams())) { + $this->saveState($args, $reflection); + } + + if ($mode === 'redirect') { + $this->saveGlobalState(); + } + + $globalState = $this->getGlobalState($destination === 'this' ? NULL : $presenterClass); + if ($current && $args) { + $tmp = $globalState + $this->params; + foreach ($args as $key => $val) { + if (http_build_query(array($val)) !== (isset($tmp[$key]) ? http_build_query(array($tmp[$key])) : '')) { + $current = FALSE; + break; + } + } + } + $args += $globalState; + } + + // ADD ACTION & SIGNAL & FLASH + $args[self::ACTION_KEY] = $action; + if (!empty($signal)) { + $args[self::SIGNAL_KEY] = $component->getParameterId($signal); + $current = $current && $args[self::SIGNAL_KEY] === $this->getParameter(self::SIGNAL_KEY); + } + if (($mode === 'redirect' || $mode === 'forward') && $this->hasFlashSession()) { + $args[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY); + } + + $this->lastCreatedRequest = new Application\Request( + $presenter, + Application\Request::FORWARD, + $args, + array(), + array() + ); + $this->lastCreatedRequestFlag = array('current' => $current); + + if ($mode === 'forward') { + return; + } + + // CONSTRUCT URL + $url = $router->constructUrl($this->lastCreatedRequest, $refUrl); + if ($url === NULL) { + unset($args[self::ACTION_KEY]); + $params = urldecode(http_build_query($args, NULL, ', ')); + throw new InvalidLinkException("No route for $presenter:$action($params)"); + } + + // make URL relative if possible + if ($mode === 'link' && $scheme === FALSE && !$this->absoluteUrls) { + $hostUrl = $refUrl->getHostUrl(); + if (strncmp($url, $hostUrl, strlen($hostUrl)) === 0) { + $url = substr($url, strlen($hostUrl)); + } + } + + return $url . $fragment; + } + + + + /** + * Converts list of arguments to named parameters. + * @param string class name + * @param string method name + * @param array arguments + * @param array supplemental arguments + * @return void + * @throws InvalidLinkException + */ + private static function argsToParams($class, $method, & $args, $supplemental = array()) + { + $i = 0; + $rm = new \ReflectionMethod($class, $method); + foreach ($rm->getParameters() as $param) { + $name = $param->getName(); + if (array_key_exists($i, $args)) { + $args[$name] = $args[$i]; + unset($args[$i]); + $i++; + + } elseif (array_key_exists($name, $args)) { + // continue with process + + } elseif (array_key_exists($name, $supplemental)) { + $args[$name] = $supplemental[$name]; + + } else { + continue; + } + + if ($args[$name] === NULL) { + continue; + } + + $def = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL; + $type = $param->isArray() ? 'array' : gettype($def); + if (!PresenterComponentReflection::convertType($args[$name], $type)) { + throw new InvalidLinkException("Invalid value for parameter '$name' in method $class::$method(), expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + + if ($args[$name] === $def || ($def === NULL && is_scalar($args[$name]) && (string) $args[$name] === '')) { + $args[$name] = NULL; // value transmit is unnecessary + } + } + + if (array_key_exists($i, $args)) { + $method = $rm->getName(); + throw new InvalidLinkException("Passed more parameters than method $class::$method() expects."); + } + } + + + + /** + * Invalid link handler. Descendant can override this method to change default behaviour. + * @param InvalidLinkException + * @return string + * @throws InvalidLinkException + */ + protected function handleInvalidLink($e) + { + if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) { + return '#'; + + } elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) { + return 'error: ' . $e->getMessage(); + + } else { // self::INVALID_LINK_EXCEPTION + throw $e; + } + } + + + + /********************* request serialization ****************d*g**/ + + + + /** + * Stores current request to session. + * @param mixed optional expiration time + * @return string key + */ + public function storeRequest($expiration = '+ 10 minutes') + { + $session = $this->getSession('Nette.Application/requests'); + do { + $key = Nette\Utils\Strings::random(5); + } while (isset($session[$key])); + + $session[$key] = array($this->getUser()->getId(), $this->request); + $session->setExpiration($expiration, $key); + return $key; + } + + + + /** + * Restores current request to session. + * @param string key + * @return void + */ + public function restoreRequest($key) + { + $session = $this->getSession('Nette.Application/requests'); + if (!isset($session[$key]) || ($session[$key][0] !== NULL && $session[$key][0] !== $this->getUser()->getId())) { + return; + } + $request = clone $session[$key][1]; + unset($session[$key]); + $request->setFlag(Application\Request::RESTORED, TRUE); + $params = $request->getParameters(); + $params[self::FLASH_KEY] = $this->getParameter(self::FLASH_KEY); + $request->setParameters($params); + $this->sendResponse(new Responses\ForwardResponse($request)); + } + + + + /********************* interface IStatePersistent ****************d*g**/ + + + + /** + * Returns array of persistent components. + * This default implementation detects components by class-level annotation @persistent(cmp1, cmp2). + * @return array + */ + public static function getPersistentComponents() + { + /*5.2*$arg = func_get_arg(0);*/ + return (array) Reflection\ClassType::from(/*5.2*$arg*//**/get_called_class()/**/) + ->getAnnotation('persistent'); + } + + + + /** + * Saves state information for all subcomponents to $this->globalState. + * @return array + */ + private function getGlobalState($forClass = NULL) + { + $sinces = & $this->globalStateSinces; + + if ($this->globalState === NULL) { + $state = array(); + foreach ($this->globalParams as $id => $params) { + $prefix = $id . self::NAME_SEPARATOR; + foreach ($params as $key => $val) { + $state[$prefix . $key] = $val; + } + } + $this->saveState($state, $forClass ? new PresenterComponentReflection($forClass) : NULL); + + if ($sinces === NULL) { + $sinces = array(); + foreach ($this->getReflection()->getPersistentParams() as $name => $meta) { + $sinces[$name] = $meta['since']; + } + } + + $components = $this->getReflection()->getPersistentComponents(); + $iterator = $this->getComponents(TRUE, 'Nette\Application\UI\IStatePersistent'); + + foreach ($iterator as $name => $component) { + if ($iterator->getDepth() === 0) { + // counts with Nette\Application\RecursiveIteratorIterator::SELF_FIRST + $since = isset($components[$name]['since']) ? $components[$name]['since'] : FALSE; // FALSE = nonpersistent + } + $prefix = $component->getUniqueId() . self::NAME_SEPARATOR; + $params = array(); + $component->saveState($params); + foreach ($params as $key => $val) { + $state[$prefix . $key] = $val; + $sinces[$prefix . $key] = $since; + } + } + + } else { + $state = $this->globalState; + } + + if ($forClass !== NULL) { + $since = NULL; + foreach ($state as $key => $foo) { + if (!isset($sinces[$key])) { + $x = strpos($key, self::NAME_SEPARATOR); + $x = $x === FALSE ? $key : substr($key, 0, $x); + $sinces[$key] = isset($sinces[$x]) ? $sinces[$x] : FALSE; + } + if ($since !== $sinces[$key]) { + $since = $sinces[$key]; + $ok = $since && (is_subclass_of($forClass, $since) || $forClass === $since); + } + if (!$ok) { + unset($state[$key]); + } + } + } + + return $state; + } + + + + /** + * Permanently saves state information for all subcomponents to $this->globalState. + * @return void + */ + protected function saveGlobalState() + { + // load lazy components + foreach ($this->globalParams as $id => $foo) { + $this->getComponent($id, FALSE); + } + + $this->globalParams = array(); + $this->globalState = $this->getGlobalState(); + } + + + + /** + * Initializes $this->globalParams, $this->signal & $this->signalReceiver, $this->action, $this->view. Called by run(). + * @return void + * @throws Nette\Application\BadRequestException if action name is not valid + */ + private function initGlobalParameters() + { + // init $this->globalParams + $this->globalParams = array(); + $selfParams = array(); + + $params = $this->request->getParameters(); + if ($this->isAjax()) { + $params += $this->request->getPost(); + } + + foreach ($params as $key => $value) { + if (!preg_match('#^((?:[a-z0-9_]+-)*)((?!\d+$)[a-z0-9_]+)$#i', $key, $matches)) { + $this->error("'Invalid parameter name '$key'"); + } + if (!$matches[1]) { + $selfParams[$key] = $value; + } else { + $this->globalParams[substr($matches[1], 0, -1)][$matches[2]] = $value; + } + } + + // init & validate $this->action & $this->view + $this->changeAction(isset($selfParams[self::ACTION_KEY]) ? $selfParams[self::ACTION_KEY] : self::DEFAULT_ACTION); + + // init $this->signalReceiver and key 'signal' in appropriate params array + $this->signalReceiver = $this->getUniqueId(); + if (isset($selfParams[self::SIGNAL_KEY])) { + $param = $selfParams[self::SIGNAL_KEY]; + if (!is_string($param)) { + $this->error('Signal name is not string.'); + } + $pos = strrpos($param, '-'); + if ($pos) { + $this->signalReceiver = substr($param, 0, $pos); + $this->signal = substr($param, $pos + 1); + } else { + $this->signalReceiver = $this->getUniqueId(); + $this->signal = $param; + } + if ($this->signal == NULL) { // intentionally == + $this->signal = NULL; + } + } + + $this->loadState($selfParams); + } + + + + /** + * Pops parameters for specified component. + * @param string component id + * @return array + */ + final public function popGlobalParameters($id) + { + if (isset($this->globalParams[$id])) { + $res = $this->globalParams[$id]; + unset($this->globalParams[$id]); + return $res; + + } else { + return array(); + } + } + + + + /********************* flash session ****************d*g**/ + + + + /** + * Checks if a flash session namespace exists. + * @return bool + */ + public function hasFlashSession() + { + return !empty($this->params[self::FLASH_KEY]) + && $this->getSession()->hasSection('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); + } + + + + /** + * Returns session namespace provided to pass temporary data between redirects. + * @return Nette\Http\SessionSection + */ + public function getFlashSession() + { + if (empty($this->params[self::FLASH_KEY])) { + $this->params[self::FLASH_KEY] = Nette\Utils\Strings::random(4); + } + return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); + } + + + + /********************* services ****************d*g**/ + + + + /** + * @return void + */ + final public function injectPrimary(Nette\DI\Container $context) + { + $this->context = $context; + } + + + + /** + * Gets the context. + * @return \SystemContainer|Nette\DI\Container + */ + final public function getContext() + { + return $this->context; + } + + + + /** + * @deprecated + */ + final public function getService($name) + { + return $this->context->getService($name); + } + + + + /** + * @return Nette\Http\Request + */ + protected function getHttpRequest() + { + return $this->context->getByType('Nette\Http\IRequest'); + } + + + + /** + * @return Nette\Http\Response + */ + protected function getHttpResponse() + { + return $this->context->getByType('Nette\Http\IResponse'); + } + + + + /** + * @return Nette\Http\Context + */ + protected function getHttpContext() + { + return $this->context->getByType('Nette\Http\Context'); + } + + + + /** + * @return Nette\Application\Application + */ + public function getApplication() + { + return $this->context->getByType('Nette\Application\Application'); + } + + + + /** + * @return Nette\Http\Session + */ + public function getSession($namespace = NULL) + { + $handler = $this->context->getByType('Nette\Http\Session'); + return $namespace === NULL ? $handler : $handler->getSection($namespace); + } + + + + /** + * @return Nette\Security\User + */ + public function getUser() + { + return $this->context->getByType('Nette\Security\User'); + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/PresenterComponent.php b/apigen/libs/Nette/Nette/Application/UI/PresenterComponent.php new file mode 100644 index 00000000000..1cd6f3cc57b --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/PresenterComponent.php @@ -0,0 +1,450 @@ +monitor('Nette\Application\UI\Presenter'); + parent::__construct($parent, $name); + } + + + + /** + * Returns the presenter where this component belongs to. + * @param bool throw exception if presenter doesn't exist? + * @return Presenter|NULL + */ + public function getPresenter($need = TRUE) + { + return $this->lookup('Nette\Application\UI\Presenter', $need); + } + + + + /** + * Returns a fully-qualified name that uniquely identifies the component + * within the presenter hierarchy. + * @return string + */ + public function getUniqueId() + { + return $this->lookupPath('Nette\Application\UI\Presenter', TRUE); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\ComponentModel\IComponent + * @return void + */ + protected function attached($presenter) + { + if ($presenter instanceof Presenter) { + $this->loadState($presenter->popGlobalParameters($this->getUniqueId())); + } + } + + + + /** + * Calls public method if exists. + * @param string + * @param array + * @return bool does method exist? + */ + protected function tryCall($method, array $params) + { + $rc = $this->getReflection(); + if ($rc->hasMethod($method)) { + $rm = $rc->getMethod($method); + if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) { + $this->checkRequirements($rm); + $rm->invokeArgs($this, $rc->combineArgs($rm, $params)); + return TRUE; + } + } + return FALSE; + } + + + + /** + * Checks for requirements such as authorization. + * @return void + */ + public function checkRequirements($element) + { + } + + + + /** + * Access to reflection. + * @return PresenterComponentReflection + */ + public /**/static/**/ function getReflection() + { + return new PresenterComponentReflection(/*5.2*$this*//**/get_called_class()/**/); + } + + + + /********************* interface IStatePersistent ****************d*g**/ + + + + /** + * Loads state informations. + * @param array + * @return void + */ + public function loadState(array $params) + { + $reflection = $this->getReflection(); + foreach ($reflection->getPersistentParams() as $name => $meta) { + if (isset($params[$name])) { // NULLs are ignored + $type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x + if (!$reflection->convertType($params[$name], $type)) { + throw new Nette\Application\BadRequestException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + $this->$name = & $params[$name]; + } else { + $params[$name] = & $this->$name; + } + } + $this->params = $params; + } + + + + /** + * Saves state informations for next request. + * @param array + * @param PresenterComponentReflection (internal, used by Presenter) + * @return void + */ + public function saveState(array & $params, $reflection = NULL) + { + $reflection = $reflection === NULL ? $this->getReflection() : $reflection; + foreach ($reflection->getPersistentParams() as $name => $meta) { + + if (isset($params[$name])) { + // injected value + + } elseif (array_key_exists($name, $params)) { // NULLs are skipped + continue; + + } elseif (!isset($meta['since']) || $this instanceof $meta['since']) { + $params[$name] = $this->$name; // object property value + + } else { + continue; // ignored parameter + } + + $type = gettype($meta['def'] === NULL ? $params[$name] : $meta['def']); // compatible with 2.0.x + if (!PresenterComponentReflection::convertType($params[$name], $type)) { + throw new InvalidLinkException("Invalid value for persistent parameter '$name' in '{$this->getName()}', expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + + if ($params[$name] === $meta['def'] || ($meta['def'] === NULL && is_scalar($params[$name]) && (string) $params[$name] === '')) { + $params[$name] = NULL; // value transmit is unnecessary + } + } + } + + + + /** + * Returns component param. + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getParameter($name = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->params; + + } elseif (isset($this->params[$name])) { + return $this->params[$name]; + + } else { + return $default; + } + } + + + + /** + * Returns a fully-qualified name that uniquely identifies the parameter. + * @param string + * @return string + */ + final public function getParameterId($name) + { + $uid = $this->getUniqueId(); + return $uid === '' ? $name : $uid . self::NAME_SEPARATOR . $name; + } + + + + /** @deprecated */ + function getParam($name = NULL, $default = NULL) + { + //trigger_error(__METHOD__ . '() is deprecated; use getParameter() instead.', E_USER_WARNING); + return func_num_args() ? $this->getParameter($name, $default) : $this->getParameter(); + } + + + + /** @deprecated */ + function getParamId($name) + { + trigger_error(__METHOD__ . '() is deprecated; use getParameterId() instead.', E_USER_WARNING); + return $this->getParameterId($name); + } + + + + /** + * Returns array of classes persistent parameters. They have public visibility and are non-static. + * This default implementation detects persistent parameters by annotation @persistent. + * @return array + */ + public static function getPersistentParams() + { + /*5.2*$arg = func_get_arg(0);*/ + $rc = new Nette\Reflection\ClassType(/*5.2*$arg*//**/get_called_class()/**/); + $params = array(); + foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $rp) { + if (!$rp->isStatic() && $rp->hasAnnotation('persistent')) { + $params[] = $rp->getName(); + } + } + return $params; + } + + + + /********************* interface ISignalReceiver ****************d*g**/ + + + + /** + * Calls signal handler method. + * @param string + * @return void + * @throws BadSignalException if there is not handler method + */ + public function signalReceived($signal) + { + if (!$this->tryCall($this->formatSignalMethod($signal), $this->params)) { + $class = get_class($this); + throw new BadSignalException("There is no handler for signal '$signal' in class $class."); + } + } + + + + /** + * Formats signal handler method name -> case sensitivity doesn't matter. + * @param string + * @return string + */ + public function formatSignalMethod($signal) + { + return $signal == NULL ? NULL : 'handle' . $signal; // intentionally == + } + + + + /********************* navigation ****************d*g**/ + + + + /** + * Generates URL to presenter, action or signal. + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array|mixed + * @return string + * @throws InvalidLinkException + */ + public function link($destination, $args = array()) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + try { + return $this->getPresenter()->createRequest($this, $destination, $args, 'link'); + + } catch (InvalidLinkException $e) { + return $this->getPresenter()->handleInvalidLink($e); + } + } + + + + /** + * Returns destination as Link object. + * @param string destination in format "[[module:]presenter:]view" or "signal!" + * @param array|mixed + * @return Link + */ + public function lazyLink($destination, $args = array()) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + + return new Link($this, $destination, $args); + } + + + + /** + * Determines whether it links to the current page. + * @param string destination in format "[[module:]presenter:]action" or "signal!" or "this" + * @param array|mixed + * @return bool + * @throws InvalidLinkException + */ + public function isLinkCurrent($destination = NULL, $args = array()) + { + if ($destination !== NULL) { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + $this->link($destination, $args); + } + return $this->getPresenter()->getLastCreatedRequestFlag('current'); + } + + + + /** + * Redirect to another presenter, action or signal. + * @param int [optional] HTTP error code + * @param string destination in format "[[module:]presenter:]view" or "signal!" + * @param array|mixed + * @return void + * @throws Nette\Application\AbortException + */ + public function redirect($code, $destination = NULL, $args = array()) + { + if (!is_numeric($code)) { // first parameter is optional + $args = $destination; + $destination = $code; + $code = NULL; + } + + if (!is_array($args)) { + $args = func_get_args(); + if (is_numeric(array_shift($args))) { + array_shift($args); + } + } + + $presenter = $this->getPresenter(); + $presenter->redirectUrl($presenter->createRequest($this, $destination, $args, 'redirect'), $code); + } + + + + /********************* interface \ArrayAccess ****************d*g**/ + + + + /** + * Adds the component to the container. + * @param string component name + * @param Nette\ComponentModel\IComponent + * @return void + */ + final public function offsetSet($name, $component) + { + $this->addComponent($component, $name); + } + + + + /** + * Returns component specified by name. Throws exception if component doesn't exist. + * @param string component name + * @return Nette\ComponentModel\IComponent + * @throws Nette\InvalidArgumentException + */ + final public function offsetGet($name) + { + return $this->getComponent($name, TRUE); + } + + + + /** + * Does component specified by name exists? + * @param string component name + * @return bool + */ + final public function offsetExists($name) + { + return $this->getComponent($name, FALSE) !== NULL; + } + + + + /** + * Removes component from the container. + * @param string component name + * @return void + */ + final public function offsetUnset($name) + { + $component = $this->getComponent($name, FALSE); + if ($component !== NULL) { + $this->removeComponent($component); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Application/UI/PresenterComponentReflection.php b/apigen/libs/Nette/Nette/Application/UI/PresenterComponentReflection.php new file mode 100644 index 00000000000..ec087914224 --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/UI/PresenterComponentReflection.php @@ -0,0 +1,174 @@ +getName() : $class; // TODO + $params = & self::$ppCache[$class]; + if ($params !== NULL) { + return $params; + } + $params = array(); + if (is_subclass_of($class, 'Nette\Application\UI\PresenterComponent')) { + $defaults = get_class_vars($class); + foreach (/**/$class::getPersistentParams()/**//*5.2*call_user_func(array($class, 'getPersistentParams'), $class)*/ as $name => $meta) { + if (is_string($meta)) { + $name = $meta; + } + $params[$name] = array( + 'def' => $defaults[$name], + 'since' => $class, + ); + } + foreach ($this->getPersistentParams(get_parent_class($class)) as $name => $param) { + if (isset($params[$name])) { + $params[$name]['since'] = $param['since']; + continue; + } + + $params[$name] = $param; + } + } + return $params; + } + + + + /** + * @param string|NULL + * @return array of persistent components. + */ + public function getPersistentComponents($class = NULL) + { + $class = $class === NULL ? $this->getName() : $class; + $components = & self::$pcCache[$class]; + if ($components !== NULL) { + return $components; + } + $components = array(); + if (is_subclass_of($class, 'Nette\Application\UI\Presenter')) { + foreach (/**/$class::getPersistentComponents()/**//*5.2*call_user_func(array($class, 'getPersistentComponents'), $class)*/ as $name => $meta) { + if (is_string($meta)) { + $name = $meta; + } + $components[$name] = array('since' => $class); + } + $components = $this->getPersistentComponents(get_parent_class($class)) + $components; + } + return $components; + } + + + + /** + * Is a method callable? It means class is instantiable and method has + * public visibility, is non-static and non-abstract. + * @param string method name + * @return bool + */ + public function hasCallableMethod($method) + { + $class = $this->getName(); + $cache = & self::$mcCache[strtolower($class . ':' . $method)]; + if ($cache === NULL) try { + $cache = FALSE; + $rm = Nette\Reflection\Method::from($class, $method); + $cache = $this->isInstantiable() && $rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic(); + } catch (\ReflectionException $e) { + } + return $cache; + } + + + + /** + * @return array + */ + public static function combineArgs(\ReflectionFunctionAbstract $method, $args) + { + $res = array(); + $i = 0; + foreach ($method->getParameters() as $param) { + $name = $param->getName(); + if (isset($args[$name])) { // NULLs are ignored + $res[$i++] = $args[$name]; + $type = $param->isArray() ? 'array' : ($param->isDefaultValueAvailable() ? gettype($param->getDefaultValue()) : 'NULL'); + if (!self::convertType($res[$i-1], $type)) { + $mName = $method instanceof \ReflectionMethod ? $method->getDeclaringClass()->getName() . '::' . $method->getName() : $method->getName(); + throw new BadRequestException("Invalid value for parameter '$name' in method $mName(), expected " . ($type === 'NULL' ? 'scalar' : $type) . "."); + } + } else { + $res[$i++] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : ($param->isArray() ? array() : NULL); + } + } + return $res; + } + + + + /** + * Non data-loss type conversion. + * @param mixed + * @param string + * @return bool + */ + public static function convertType(& $val, $type) + { + if ($val === NULL || is_object($val)) { + // ignore + } elseif ($type === 'array') { + if (!is_array($val)) { + return FALSE; + } + } elseif (!is_scalar($val)) { + return FALSE; + + } elseif ($type !== 'NULL') { + $old = $val = ($val === FALSE ? '0' : (string) $val); + settype($val, $type); + if ($old !== ($val === FALSE ? '0' : (string) $val)) { + return FALSE; // data-loss occurs + } + } + return TRUE; + } + +} diff --git a/apigen/libs/Nette/Nette/Application/exceptions.php b/apigen/libs/Nette/Nette/Application/exceptions.php new file mode 100644 index 00000000000..d3d17606fff --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/exceptions.php @@ -0,0 +1,92 @@ +previous = $previous; + parent::__construct($message, $code); + } else { + parent::__construct($message, $code, $previous); + } + } + */ +} + + + +/** + * The exception that is thrown when a presenter cannot be loaded. + */ +class InvalidPresenterException extends \Exception +{ +} + + + +/** + * Bad HTTP / presenter request exception. + */ +class BadRequestException extends \Exception +{ + /** @var int */ + protected $defaultCode = 404; + + + public function __construct($message = '', $code = 0, \Exception $previous = NULL) + { + if ($code < 200 || $code > 504) { + $code = $this->defaultCode; + } + + /*5.2*if (PHP_VERSION_ID < 50300) { + $this->previous = $previous; + parent::__construct($message, $code); + } else*/ { + parent::__construct($message, $code, $previous); + } + } + +} + + + +/** + * Forbidden request exception - access denied. + */ +class ForbiddenRequestException extends BadRequestException +{ + /** @var int */ + protected $defaultCode = 403; + +} diff --git a/apigen/libs/Nette/Nette/Application/templates/error.phtml b/apigen/libs/Nette/Nette/Application/templates/error.phtml new file mode 100644 index 00000000000..62eb652b93c --- /dev/null +++ b/apigen/libs/Nette/Nette/Application/templates/error.phtml @@ -0,0 +1,37 @@ + array('Oops...', 'Your browser sent a request that this server could not understand or process.'), + 403 => array('Access Denied', 'You do not have permission to view this page. Please try contact the web site administrator if you believe you should be able to view this page.'), + 404 => array('Page Not Found', 'The page you requested could not be found. It is possible that the address is incorrect, or that the page no longer exists. Please use a search engine to find what you are looking for.'), + 405 => array('Method Not Allowed', 'The requested method is not allowed for the URL.'), + 410 => array('Page Not Found', 'The page you requested has been taken off the site. We apologize for the inconvenience.'), + 500 => array('Server Error', 'We\'re sorry! The server encountered an internal error and was unable to complete your request. Please try again later.'), +); +$message = isset($messages[$code]) ? $messages[$code] : $messages[0]; + +?> + + + + + +<?php echo $message[0] ?> + +

+ +

+ +

error

diff --git a/apigen/libs/Nette/Nette/Caching/Cache.php b/apigen/libs/Nette/Nette/Caching/Cache.php new file mode 100644 index 00000000000..d746acb0016 --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Cache.php @@ -0,0 +1,428 @@ +storage = $storage; + $this->namespace = $namespace . self::NAMESPACE_SEPARATOR; + } + + + + /** + * Returns cache storage. + * @return IStorage + */ + public function getStorage() + { + return $this->storage; + } + + + + /** + * Returns cache namespace. + * @return string + */ + public function getNamespace() + { + return (string) substr($this->namespace, 0, -1); + } + + + + /** + * Returns new nested cache object. + * @param string + * @return Cache + */ + public function derive($namespace) + { + $derived = new static($this->storage, $this->namespace . $namespace); + return $derived; + } + + + + /** + * Reads the specified item from the cache or generate it. + * @param mixed key + * @param callable + * @return mixed|NULL + */ + public function load($key, $fallback = NULL) + { + $data = $this->storage->read($this->generateKey($key)); + if ($data === NULL && $fallback) { + return $this->save($key, new Nette\Callback($fallback)); + } + return $data; + } + + + + /** + * Writes item into the cache. + * Dependencies are: + * - Cache::PRIORITY => (int) priority + * - Cache::EXPIRATION => (timestamp) expiration + * - Cache::SLIDING => (bool) use sliding expiration? + * - Cache::TAGS => (array) tags + * - Cache::FILES => (array|string) file names + * - Cache::ITEMS => (array|string) cache items + * - Cache::CONSTS => (array|string) cache items + * + * @param mixed key + * @param mixed value + * @param array dependencies + * @return mixed value itself + * @throws Nette\InvalidArgumentException + */ + public function save($key, $data, array $dp = NULL) + { + $this->release(); + $key = $this->generateKey($key); + + if ($data instanceof Nette\Callback || $data instanceof \Closure) { + $this->storage->lock($key); + $data = Nette\Callback::create($data)->invokeArgs(array(&$dp)); + } + + if ($data === NULL) { + $this->storage->remove($key); + } else { + $this->storage->write($key, $data, $this->completeDependencies($dp, $data)); + return $data; + } + } + + + + private function completeDependencies($dp, $data) + { + if (is_object($data)) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data), + Nette\Reflection\ClassType::from($data)->getAnnotation('serializationVersion')); + } + + // convert expire into relative amount of seconds + if (isset($dp[Cache::EXPIRATION])) { + $dp[Cache::EXPIRATION] = Nette\DateTime::from($dp[Cache::EXPIRATION])->format('U') - time(); + } + + // convert FILES into CALLBACKS + if (isset($dp[self::FILES])) { + //clearstatcache(); + foreach (array_unique((array) $dp[self::FILES]) as $item) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); // @ - stat may fail + } + unset($dp[self::FILES]); + } + + // add namespaces to items + if (isset($dp[self::ITEMS])) { + $dp[self::ITEMS] = array_unique((array) $dp[self::ITEMS]); + foreach ($dp[self::ITEMS] as $k => $item) { + $dp[self::ITEMS][$k] = $this->generateKey($item); + } + } + + // convert CONSTS into CALLBACKS + if (isset($dp[self::CONSTS])) { + foreach (array_unique((array) $dp[self::CONSTS]) as $item) { + $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item)); + } + unset($dp[self::CONSTS]); + } + + if (!is_array($dp)) { + $dp = array(); + } + return $dp; + } + + + + /** + * Removes item from the cache. + * @param mixed key + * @return void + */ + public function remove($key) + { + $this->save($key, NULL); + } + + + + /** + * Removes items from the cache by conditions. + * Conditions are: + * - Cache::PRIORITY => (int) priority + * - Cache::TAGS => (array) tags + * - Cache::ALL => TRUE + * + * @param array + * @return void + */ + public function clean(array $conds = NULL) + { + $this->release(); + $this->storage->clean((array) $conds); + } + + + + /** + * Caches results of function/method calls. + * @param mixed + * @return mixed + */ + public function call($function) + { + $key = func_get_args(); + return $this->load($key, function() use ($function, $key) { + array_shift($key); + return Nette\Callback::create($function)->invokeArgs($key); + }); + } + + + + /** + * Caches results of function/method calls. + * @param mixed + * @param array dependencies + * @return Closure + */ + public function wrap($function, array $dp = NULL) + { + $cache = $this; + return function() use ($cache, $function, $dp) { + $key = array($function, func_get_args()); + $data = $cache->load($key); + if ($data === NULL) { + $data = $cache->save($key, Nette\Callback::create($function)->invokeArgs($key[1]), $dp); + } + return $data; + }; + } + + + + /** + * Starts the output cache. + * @param mixed key + * @return OutputHelper|NULL + */ + public function start($key) + { + $data = $this->load($key); + if ($data === NULL) { + return new OutputHelper($this, $key); + } + echo $data; + } + + + + /** + * Generates internal cache key. + * + * @param string + * @return string + */ + protected function generateKey($key) + { + return $this->namespace . md5(is_scalar($key) ? $key : serialize($key)); + } + + + + /********************* interface ArrayAccess ****************d*g**/ + + + + /** + * Inserts (replaces) item into the cache (\ArrayAccess implementation). + * @param mixed key + * @param mixed + * @return void + * @throws Nette\InvalidArgumentException + */ + public function offsetSet($key, $data) + { + $this->save($key, $data); + } + + + + /** + * Retrieves the specified item from the cache or NULL if the key is not found (\ArrayAccess implementation). + * @param mixed key + * @return mixed|NULL + * @throws Nette\InvalidArgumentException + */ + public function offsetGet($key) + { + $key = is_scalar($key) ? (string) $key : serialize($key); + if ($this->key !== $key) { + $this->key = $key; + $this->data = $this->load($key); + } + return $this->data; + } + + + + /** + * Exists item in cache? (\ArrayAccess implementation). + * @param mixed key + * @return bool + * @throws Nette\InvalidArgumentException + */ + public function offsetExists($key) + { + $this->release(); + return $this->offsetGet($key) !== NULL; + } + + + + /** + * Removes the specified item from the cache. + * @param mixed key + * @return void + * @throws Nette\InvalidArgumentException + */ + public function offsetUnset($key) + { + $this->save($key, NULL); + } + + + + /** + * Discards the internal cache used by ArrayAccess. + * @return void + */ + public function release() + { + $this->key = $this->data = NULL; + } + + + + /********************* dependency checkers ****************d*g**/ + + + + /** + * Checks CALLBACKS dependencies. + * @param array + * @return bool + */ + public static function checkCallbacks($callbacks) + { + foreach ($callbacks as $callback) { + $func = array_shift($callback); + if (!call_user_func_array($func, $callback)) { + return FALSE; + } + } + return TRUE; + } + + + + /** + * Checks CONSTS dependency. + * @param string + * @param mixed + * @return bool + */ + private static function checkConst($const, $value) + { + return defined($const) && constant($const) === $value; + } + + + + /** + * Checks FILES dependency. + * @param string + * @param int + * @return bool + */ + private static function checkFile($file, $time) + { + return @filemtime($file) == $time; // @ - stat may fail + } + + + + /** + * Checks object @serializationVersion label. + * @param string + * @param mixed + * @return bool + */ + private static function checkSerializationVersion($class, $value) + { + return Nette\Reflection\ClassType::from($class)->getAnnotation('serializationVersion') === $value; + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/IStorage.php b/apigen/libs/Nette/Nette/Caching/IStorage.php new file mode 100644 index 00000000000..cf67751af3c --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/IStorage.php @@ -0,0 +1,63 @@ +cache = $cache; + $this->key = $key; + ob_start(); + } + + + + /** + * Stops and saves the cache. + * @param array dependencies + * @return void + */ + public function end(array $dp = NULL) + { + if ($this->cache === NULL) { + throw new Nette\InvalidStateException('Output cache has already been saved.'); + } + $this->cache->save($this->key, ob_get_flush(), (array) $dp + (array) $this->dependencies); + $this->cache = NULL; + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/Storages/DevNullStorage.php b/apigen/libs/Nette/Nette/Caching/Storages/DevNullStorage.php new file mode 100644 index 00000000000..1d6dfd1a80d --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Storages/DevNullStorage.php @@ -0,0 +1,81 @@ + 0, + self::PRIORITY => 1, + self::ENTRIES => 2, + self::DATA => 3, + ); + + + + /** + * @param string Directory location with journal file + */ + public function __construct($dir) + { + $this->file = $dir . '/' . self::FILE; + + // Create jorunal file when not exists + if (!file_exists($this->file)) { + $init = @fopen($this->file, 'xb'); // intentionally @ + if (!$init) { + clearstatcache(); + if (!file_exists($this->file)) { + throw new Nette\InvalidStateException("Cannot create journal file $this->file."); + } + } else { + $writen = fwrite($init, pack('N2', self::FILE_MAGIC, $this->lastNode)); + fclose($init); + if ($writen !== self::INT32_SIZE * 2) { + throw new Nette\InvalidStateException("Cannot write journal header."); + } + } + } + + $this->handle = fopen($this->file, 'r+b'); + + if (!$this->handle) { + throw new Nette\InvalidStateException("Cannot open journal file '$this->file'."); + } + + if (!flock($this->handle, LOCK_SH)) { + throw new Nette\InvalidStateException('Cannot acquire shared lock on journal.'); + } + + $header = stream_get_contents($this->handle, 2 * self::INT32_SIZE, 0); + + flock($this->handle, LOCK_UN); + + list(, $fileMagic, $this->lastNode) = unpack('N2', $header); + + if ($fileMagic !== self::FILE_MAGIC) { + fclose($this->handle); + $this->handle = false; + throw new Nette\InvalidStateException("Malformed journal file '$this->file'."); + } + } + + + + /** + * @return void + */ + public function __destruct() + { + if ($this->handle) { + $this->headerCommit(); + flock($this->handle, LOCK_UN); // Since PHP 5.3.3 is manual unlock necesary + fclose($this->handle); + $this->handle = false; + } + } + + + + /** + * Writes entry information into the journal. + * @param string + * @param array + * @return void + */ + public function write($key, array $dependencies) + { + $this->lock(); + + $priority = !isset($dependencies[Cache::PRIORITY]) ? FALSE : (int) $dependencies[Cache::PRIORITY]; + $tags = empty($dependencies[Cache::TAGS]) ? FALSE : (array) $dependencies[Cache::TAGS]; + + $exists = FALSE; + $keyHash = crc32($key); + list($entriesNodeId, $entriesNode) = $this->findIndexNode(self::ENTRIES, $keyHash); + + if (isset($entriesNode[$keyHash])) { + $entries = $this->mergeIndexData($entriesNode[$keyHash]); + foreach ($entries as $link => $foo) { + $dataNode = $this->getNode($link >> self::BITROT); + if ($dataNode[$link][self::KEY] === $key) { + if ($dataNode[$link][self::TAGS] == $tags && $dataNode[$link][self::PRIORITY] === $priority) { // intentionally ==, the order of tags does not matter + if ($dataNode[$link][self::DELETED]) { + $dataNode[$link][self::DELETED] = FALSE; + $this->saveNode($link >> self::BITROT, $dataNode); + } + $exists = TRUE; + } else { // Alredy exists, but with other tags or priority + $toDelete = array(); + foreach ($dataNode[$link][self::TAGS] as $tag) { + $toDelete[self::TAGS][$tag][$link] = TRUE; + } + if ($dataNode[$link][self::PRIORITY] !== FALSE) { + $toDelete[self::PRIORITY][$dataNode[$link][self::PRIORITY]][$link] = TRUE; + } + $toDelete[self::ENTRIES][$keyHash][$link] = TRUE; + $this->cleanFromIndex($toDelete); + $entriesNode = $this->getNode($entriesNodeId); // Node was changed, get again + unset($dataNode[$link]); + $this->saveNode($link >> self::BITROT, $dataNode); + } + break; + } + } + } + + if ($exists === FALSE) { + // Magical constants + $requiredSize = strlen($key) + 75; + if ($tags) { + foreach ($tags as $tag) { + $requiredSize += strlen($tag) + 13; + } + } + $requiredSize += $priority ? 10 : 1; + + $freeDataNode = $this->findFreeDataNode($requiredSize); + $data = $this->getNode($freeDataNode); + + if ($data === FALSE) { + $data = array( + self::INFO => array( + self::LAST_INDEX => ($freeDataNode << self::BITROT), + self::TYPE => self::DATA, + ) + ); + } + + $dataNodeKey = ++$data[self::INFO][self::LAST_INDEX]; + $data[$dataNodeKey] = array( + self::KEY => $key, + self::TAGS => $tags ? $tags : array(), + self::PRIORITY => $priority, + self::DELETED => FALSE, + ); + + $this->saveNode($freeDataNode, $data); + + // Save to entries tree, ... + $entriesNode[$keyHash][$dataNodeKey] = 1; + $this->saveNode($entriesNodeId, $entriesNode); + + // ...tags tree... + if ($tags) { + foreach ($tags as $tag) { + list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); + $node[$tag][$dataNodeKey] = 1; + $this->saveNode($nodeId, $node); + } + } + + // ...and priority tree. + if ($priority) { + list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); + $node[$priority][$dataNodeKey] = 1; + $this->saveNode($nodeId, $node); + } + } + + $this->commit(); + $this->unlock(); + } + + + + /** + * Cleans entries from journal. + * @param array + * @return array of removed items or NULL when performing a full cleanup + */ + public function clean(array $conditions) + { + $this->lock(); + + if (!empty($conditions[Cache::ALL])) { + $this->nodeCache = $this->nodeChanged = $this->dataNodeFreeSpace = array(); + $this->deleteAll(); + $this->unlock(); + return; + } + + $toDelete = array( + self::TAGS => array(), + self::PRIORITY => array(), + self::ENTRIES => array() + ); + + $entries = array(); + + if (!empty($conditions[Cache::TAGS])) { + $entries = $this->cleanTags((array) $conditions[Cache::TAGS], $toDelete); + } + + if (isset($conditions[Cache::PRIORITY])) { + $this->arrayAppend($entries, $this->cleanPriority((int) $conditions[Cache::PRIORITY], $toDelete)); + } + + $this->deletedLinks = array(); + $this->cleanFromIndex($toDelete); + + $this->commit(); + $this->unlock(); + + return $entries; + } + + + + /** + * Cleans entries from journal by tags. + * @return array of removed items + */ + private function cleanTags(array $tags, array &$toDelete) + { + $entries = array(); + + foreach ($tags as $tag) { + list($nodeId, $node) = $this->findIndexNode(self::TAGS, $tag); + + if (isset($node[$tag])) { + $ent = $this->cleanLinks($this->mergeIndexData($node[$tag]), $toDelete); + $this->arrayAppend($entries, $ent); + } + } + + return $entries; + } + + + + /** + * Cleans entries from journal by priority. + * @param integer + * @param array + * @return array of removed items + */ + private function cleanPriority($priority, array &$toDelete) + { + list($nodeId, $node) = $this->findIndexNode(self::PRIORITY, $priority); + + ksort($node); + + $allData = array(); + + foreach ($node as $prior => $data) { + if ($prior === self::INFO) { + continue; + } elseif ($prior > $priority) { + break; + } + + $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); + } + + $nodeInfo = $node[self::INFO]; + while ($nodeInfo[self::PREV_NODE] !== -1) { + $nodeId = $nodeInfo[self::PREV_NODE]; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $nodeId."); + } + break; + } + + $nodeInfo = $node[self::INFO]; + unset($node[self::INFO]); + + foreach ($node as $prior => $data) { + $this->arrayAppendKeys($allData, $this->mergeIndexData($data)); + } + } + + return $this->cleanLinks($allData, $toDelete); + } + + + + /** + * Cleans links from $data. + * @param array + * @param array + * @return array of removed items + */ + private function cleanLinks(array $data, array &$toDelete) + { + $return = array(); + + $data = array_keys($data); + sort($data); + $max = count($data); + $data[] = 0; + $i = 0; + + while ($i < $max) { + $searchLink = $data[$i]; + + if (isset($this->deletedLinks[$searchLink])) { + ++$i; + continue; + } + + $nodeId = $searchLink >> self::BITROT; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException('Cannot load node number ' . ($nodeId) . '.'); + } + ++$i; + continue; + } + + do { + $link = $data[$i]; + + if (!isset($node[$link])) { + if (self::$debug) { + throw new Nette\InvalidStateException("Link with ID $searchLink is not in node ". ($nodeId) . '.'); + } + continue; + } elseif (isset($this->deletedLinks[$link])) { + continue; + } + + $nodeLink = &$node[$link]; + if (!$nodeLink[self::DELETED]) { + $nodeLink[self::DELETED] = TRUE; + $return[] = $nodeLink[self::KEY]; + } else { + foreach ($nodeLink[self::TAGS] as $tag) { + $toDelete[self::TAGS][$tag][$link] = TRUE; + } + if ($nodeLink[self::PRIORITY] !== FALSE) { + $toDelete[self::PRIORITY][$nodeLink[self::PRIORITY]][$link] = TRUE; + } + $toDelete[self::ENTRIES][crc32($nodeLink[self::KEY])][$link] = TRUE; + unset($node[$link]); + $this->deletedLinks[$link] = TRUE; + } + } while (($data[++$i] >> self::BITROT) === $nodeId); + + $this->saveNode($nodeId, $node); + } + + return $return; + } + + + + /** + * Remove links to deleted keys from index. + * @param array + */ + private function cleanFromIndex(array $toDeleteFromIndex) + { + foreach ($toDeleteFromIndex as $type => $toDelete) { + ksort($toDelete); + + while (!empty($toDelete)) { + reset($toDelete); + $searchKey = key($toDelete); + list($masterNodeId, $masterNode) = $this->findIndexNode($type, $searchKey); + + if (!isset($masterNode[$searchKey])) { + if (self::$debug) { + throw new Nette\InvalidStateException('Bad index.'); + } + unset($toDelete[$searchKey]); + continue; + } + + foreach ($toDelete as $key => $links) { + if (isset($masterNode[$key])) { + foreach ($links as $link => $foo) { + if (isset($masterNode[$key][$link])) { + unset($masterNode[$key][$link], $links[$link]); + } + } + + if (!empty($links) && isset($masterNode[$key][self::INDEX_DATA])) { + $this->cleanIndexData($masterNode[$key][self::INDEX_DATA], $links, $masterNode[$key]); + } + + if (empty($masterNode[$key])) { + unset($masterNode[$key]); + } + unset($toDelete[$key]); + } else { + break; + } + } + $this->saveNode($masterNodeId, $masterNode); + } + } + } + + + + /** + * Merge data with index data in other nodes. + * @param array + * @return array of merged items + */ + private function mergeIndexData(array $data) + { + while (isset($data[self::INDEX_DATA])) { + $id = $data[self::INDEX_DATA]; + unset($data[self::INDEX_DATA]); + $childNode = $this->getNode($id); + + if ($childNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $id."); + } + break; + } + + $this->arrayAppendKeys($data, $childNode[self::INDEX_DATA]); + } + + return $data; + } + + + + /** + * Cleans links from other nodes. + * @param int + * @param array + * @param array + * @return void + */ + private function cleanIndexData($nextNodeId, array $links, &$masterNodeLink) + { + $prev = -1; + + while ($nextNodeId && !empty($links)) { + $nodeId = $nextNodeId; + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $nodeId."); + } + break; + } + + foreach ($links as $link => $foo) { + if (isset($node[self::INDEX_DATA][$link])) { + unset($node[self::INDEX_DATA][$link], $links[$link]); + } + } + + if (isset($node[self::INDEX_DATA][self::INDEX_DATA])) { + $nextNodeId = $node[self::INDEX_DATA][self::INDEX_DATA]; + } else { + $nextNodeId = FALSE; + } + + if (empty($node[self::INDEX_DATA]) || (count($node[self::INDEX_DATA]) === 1 && $nextNodeId)) { + if ($prev === -1) { + if ($nextNodeId === FALSE) { + unset($masterNodeLink[self::INDEX_DATA]); + } else { + $masterNodeLink[self::INDEX_DATA] = $nextNodeId; + } + } else { + $prevNode = $this->getNode($prev); + if ($prevNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $prev."); + } + } else { + if ($nextNodeId === FALSE) { + unset($prevNode[self::INDEX_DATA][self::INDEX_DATA]); + if (empty($prevNode[self::INDEX_DATA])) { + unset($prevNode[self::INDEX_DATA]); + } + } else { + $prevNode[self::INDEX_DATA][self::INDEX_DATA] = $nextNodeId; + } + + $this->saveNode($prev, $prevNode); + } + } + unset($node[self::INDEX_DATA]); + } else { + $prev = $nodeId; + } + + $this->saveNode($nodeId, $node); + } + } + + + + /** + * Get node from journal. + * @param integer + * @return array + */ + private function getNode($id) + { + // Load from cache + if (isset($this->nodeCache[$id])) { + return $this->nodeCache[$id]; + } + + $binary = stream_get_contents($this->handle, self::NODE_SIZE, self::HEADER_SIZE + self::NODE_SIZE * $id); + + if (empty($binary)) { + // empty node, no Exception + return FALSE; + } + + list(, $magic, $lenght) = unpack('N2', $binary); + if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { + if (!empty($magic)) { + if (self::$debug) { + throw new Nette\InvalidStateException("Node $id has malformed header."); + } + $this->deleteNode($id); + } + return FALSE; + } + + $data = substr($binary, 2 * self::INT32_SIZE, $lenght - 2 * self::INT32_SIZE); + + $node = @unserialize($data); // intentionally @ + if ($node === FALSE) { + $this->deleteNode($id); + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot deserialize node number $id."); + } + return FALSE; + } + + // Save to cache and return + return $this->nodeCache[$id] = $node; + } + + + + /** + * Save node to cache. + * @param integer + * @param array + * @return void + */ + private function saveNode($id, array $node) + { + if (count($node) === 1) { // Nod contains only INFO + $nodeInfo = $node[self::INFO]; + if ($nodeInfo[self::TYPE] !== self::DATA) { + + if ($nodeInfo[self::END] !== -1) { + $this->nodeCache[$id] = $node; + $this->nodeChanged[$id] = TRUE; + return; + } + + if ($nodeInfo[self::MAX] === -1) { + $max = PHP_INT_MAX; + } else { + $max = $nodeInfo[self::MAX]; + } + + list(, , $parentId) = $this->findIndexNode($nodeInfo[self::TYPE], $max, $id); + if ($parentId !== -1 && $parentId !== $id) { + $parentNode = $this->getNode($parentId); + if ($parentNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $parentId."); + } + } else { + if ($parentNode[self::INFO][self::END] === $id) { + if (count($parentNode) === 1) { + $parentNode[self::INFO][self::END] = -1; + } else { + end($parentNode); + $lastKey = key($parentNode); + $parentNode[self::INFO][self::END] = $parentNode[$lastKey]; + unset($parentNode[$lastKey]); + } + } else { + unset($parentNode[$nodeInfo[self::MAX]]); + } + + $this->saveNode($parentId, $parentNode); + } + } + + if ($nodeInfo[self::TYPE] === self::PRIORITY) { // only priority tree has link to prevNode + if ($nodeInfo[self::MAX] === -1) { + if ($nodeInfo[self::PREV_NODE] !== -1) { + $prevNode = $this->getNode($nodeInfo[self::PREV_NODE]); + if ($prevNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException('Cannot load node number ' . $nodeInfo[self::PREV_NODE] . '.'); + } + } else { + $prevNode[self::INFO][self::MAX] = -1; + $this->saveNode($nodeInfo[self::PREV_NODE], $prevNode); + } + } + } else { + list($nextId, $nextNode) = $this->findIndexNode($nodeInfo[self::TYPE], $nodeInfo[self::MAX] + 1, NULL, $id); + if ($nextId !== -1 && $nextId !== $id) { + $nextNode[self::INFO][self::PREV_NODE] = $nodeInfo[self::PREV_NODE]; + $this->saveNode($nextId, $nextNode); + } + } + } + } + $this->nodeCache[$id] = FALSE; + } else { + $this->nodeCache[$id] = $node; + } + $this->nodeChanged[$id] = TRUE; + } + + + + /** + * Commit all changed nodes from cache to journal file. + * @return void + */ + private function commit() + { + do { + foreach ($this->nodeChanged as $id => $foo) { + if ($this->prepareNode($id, $this->nodeCache[$id])) { + unset($this->nodeChanged[$id]); + } + } + } while (!empty($this->nodeChanged)); + + foreach ($this->toCommit as $node => $str) { + $this->commitNode($node, $str); + } + $this->toCommit = array(); + } + + + + /** + * Prepare node to journal file structure. + * @param integer + * @param array|bool + * @return bool Sucessfully commited + */ + private function prepareNode($id, $node) + { + if ($node === FALSE) { + if ($id < $this->lastNode) { + $this->lastNode = $id; + } + unset($this->nodeCache[$id]); + unset($this->dataNodeFreeSpace[$id]); + $this->deleteNode($id); + return TRUE; + } + + $data = serialize($node); + $dataSize = strlen($data) + 2 * self::INT32_SIZE; + + $isData = $node[self::INFO][self::TYPE] === self::DATA; + if ($dataSize > self::NODE_SIZE) { + if ($isData) { + throw new Nette\InvalidStateException('Saving node is bigger than maximum node size.'); + } else { + $this->bisectNode($id, $node); + return FALSE; + } + } + + $this->toCommit[$id] = pack('N2', $isData ? self::DATA_MAGIC : self::INDEX_MAGIC, $dataSize) . $data; + + if ($this->lastNode < $id) { + $this->lastNode = $id; + } + if ($isData) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE - $dataSize; + } + + return TRUE; + } + + + + /** + * Commit node string to journal file. + * @param integer + * @param string + * @return void + */ + private function commitNode($id, $str) + { + fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); + $writen = fwrite($this->handle, $str); + if ($writen === FALSE) { + throw new Nette\InvalidStateException("Cannot write node number $id to journal."); + } + } + + + + /** + * Find right node in B+tree. . + * @param string Tree type (TAGS, PRIORITY or ENTRIES) + * @param int Searched item + * @return array Node + */ + private function findIndexNode($type, $search, $childId = NULL, $prevId = NULL) + { + $nodeId = self::$startNode[$type]; + + $parentId = -1; + while (TRUE) { + $node = $this->getNode($nodeId); + + if ($node === FALSE) { + return array( + $nodeId, + array( + self::INFO => array( + self::TYPE => $type, + self::IS_LEAF => TRUE, + self::PREV_NODE => -1, + self::END => -1, + self::MAX => -1, + ) + ), + $parentId, + ); // Init empty node + } + + if ($node[self::INFO][self::IS_LEAF] || $nodeId === $childId || $node[self::INFO][self::PREV_NODE] === $prevId) { + return array($nodeId, $node, $parentId); + } + + $parentId = $nodeId; + + if (isset($node[$search])) { + $nodeId = $node[$search]; + } else { + foreach ($node as $key => $childNode) { + if ($key > $search and $key !== self::INFO) { + $nodeId = $childNode; + continue 2; + } + } + + $nodeId = $node[self::INFO][self::END]; + } + } + } + + + + /** + * Find complete free node. + * @param integer + * @return array|integer Node ID + */ + private function findFreeNode($count = 1) + { + $id = $this->lastNode; + $nodesId = array(); + + do { + if (isset($this->nodeCache[$id])) { + ++$id; + continue; + } + + $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; + + $binary = stream_get_contents($this->handle, self::INT32_SIZE, $offset); + + if (empty($binary)) { + $nodesId[] = $id; + } else { + list(, $magic) = unpack('N', $binary); + if ($magic !== self::INDEX_MAGIC && $magic !== self::DATA_MAGIC) { + $nodesId[] = $id; + } + } + + ++$id; + } while (count($nodesId) !== $count); + + if ($count === 1) { + return $nodesId[0]; + } else { + return $nodesId; + } + } + + + + /** + * Find free data node that has $size bytes of free space. + * @param integer size in bytes + * @return integer Node ID + */ + private function findFreeDataNode($size) + { + foreach ($this->dataNodeFreeSpace as $id => $freeSpace) { + if ($freeSpace > $size) { + return $id; + } + } + + $id = self::$startNode[self::DATA]; + while (TRUE) { + if (isset($this->dataNodeFreeSpace[$id]) || isset($this->nodeCache[$id])) { + ++$id; + continue; + } + + $offset = self::HEADER_SIZE + self::NODE_SIZE * $id; + $binary = stream_get_contents($this->handle, 2 * self::INT32_SIZE, $offset); + + if (empty($binary)) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; + return $id; + } + + list(, $magic, $nodeSize) = unpack('N2', $binary); + if (empty($magic)) { + $this->dataNodeFreeSpace[$id] = self::NODE_SIZE; + return $id; + } elseif ($magic === self::DATA_MAGIC) { + $freeSpace = self::NODE_SIZE - $nodeSize; + $this->dataNodeFreeSpace[$id] = $freeSpace; + + if ($freeSpace > $size) { + return $id; + } + } + + ++$id; + } + } + + + + /** + * Bisect node or when has only one key, move part to data node. + * @param integer Node ID + * @param array Node + * @return void + */ + private function bisectNode($id, array $node) + { + $nodeInfo = $node[self::INFO]; + unset($node[self::INFO]); + + if (count($node) === 1) { + $key = key($node); + + $dataId = $this->findFreeDataNode(self::NODE_SIZE); + $this->saveNode($dataId, array( + self::INDEX_DATA => $node[$key], + self::INFO => array( + self::TYPE => self::DATA, + self::LAST_INDEX => ($dataId << self::BITROT), + ))); + + unset($node[$key]); + $node[$key][self::INDEX_DATA] = $dataId; + $node[self::INFO] = $nodeInfo; + + $this->saveNode($id, $node); + return; + } + + ksort($node); + $halfCount = ceil(count($node) / 2); + + list($first, $second) = array_chunk($node, $halfCount, TRUE); + + end($first); + $halfKey = key($first); + + if ($id <= 2) { // Root + list($firstId, $secondId) = $this->findFreeNode(2); + + $first[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => -1, + self::END => -1, + self::MAX => $halfKey, + ); + $this->saveNode($firstId, $first); + + $second[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $firstId, + self::END => $nodeInfo[self::END], + self::MAX => -1, + ); + $this->saveNode($secondId, $second); + + $parentNode = array( + self::INFO => array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => FALSE, + self::PREV_NODE => -1, + self::END => $secondId, + self::MAX => -1, + ), + $halfKey => $firstId, + ); + $this->saveNode($id, $parentNode); + } else { + $firstId = $this->findFreeNode(); + + $first[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $nodeInfo[self::PREV_NODE], + self::END => -1, + self::MAX => $halfKey, + ); + $this->saveNode($firstId, $first); + + $second[self::INFO] = array( + self::TYPE => $nodeInfo[self::TYPE], + self::IS_LEAF => $nodeInfo[self::IS_LEAF], + self::PREV_NODE => $firstId, + self::END => $nodeInfo[self::END], + self::MAX => $nodeInfo[self::MAX], + ); + $this->saveNode($id, $second); + + list(,, $parent) = $this->findIndexNode($nodeInfo[self::TYPE], $halfKey); + $parentNode = $this->getNode($parent); + if ($parentNode === FALSE) { + if (self::$debug) { + throw new Nette\InvalidStateException("Cannot load node number $parent."); + } + } else { + $parentNode[$halfKey] = $firstId; + ksort($parentNode); // Parent index must be always sorted. + $this->saveNode($parent, $parentNode); + } + } + } + + + + /** + * Commit header to journal file. + * @return void + */ + private function headerCommit() + { + fseek($this->handle, self::INT32_SIZE); + @fwrite($this->handle, pack('N', $this->lastNode)); // intentionally @, save is not necceseary + } + + + + /** + * Remove node from journal file. + * @param integer + * @return void + */ + private function deleteNode($id) + { + fseek($this->handle, 0, SEEK_END); + $end = ftell($this->handle); + + if ($end <= (self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { + $packedNull = pack('N', 0); + + do { + $binary = stream_get_contents($this->handle, self::INT32_SIZE, (self::HEADER_SIZE + self::NODE_SIZE * --$id)); + } while (empty($binary) || $binary === $packedNull); + + if (!ftruncate($this->handle, self::HEADER_SIZE + self::NODE_SIZE * ($id + 1))) { + throw new Nette\InvalidStateException('Cannot truncate journal file.'); + } + } else { + fseek($this->handle, self::HEADER_SIZE + self::NODE_SIZE * $id); + $writen = fwrite($this->handle, pack('N', 0)); + if ($writen !== self::INT32_SIZE) { + throw new Nette\InvalidStateException("Cannot delete node number $id from journal."); + } + } + } + + + + /** + * Complete delete all nodes from file. + * @return void + */ + private function deleteAll() + { + if (!ftruncate($this->handle, self::HEADER_SIZE)) { + throw new Nette\InvalidStateException('Cannot truncate journal file.'); + } + } + + + + /** + * Lock file for writing and reading and delete node cache when file has changed. + * @return void + */ + private function lock() + { + if (!$this->handle) { + throw new Nette\InvalidStateException('File journal file is not opened'); + } + + if (!flock($this->handle, LOCK_EX)) { + throw new Nette\InvalidStateException('Cannot acquire exclusive lock on journal.'); + } + + if ($this->lastModTime !== NULL) { + clearstatcache(); + if ($this->lastModTime < @filemtime($this->file)) { // intentionally @ + $this->nodeCache = $this->dataNodeFreeSpace = array(); + } + } + } + + + + /** + * Unlock file and save last modified time. + * @return void + */ + private function unlock() + { + if ($this->handle) { + fflush($this->handle); + flock($this->handle, LOCK_UN); + clearstatcache(); + $this->lastModTime = @filemtime($this->file); // intentionally @ + } + } + + + + /** + * Append $append to $array. + * This function is much faster then $array = array_merge($array, $append) + * @param array + * @param array + * @return void + */ + private function arrayAppend(array &$array, array $append) + { + foreach ($append as $value) { + $array[] = $value; + } + } + + + + /** + * Append $append to $array with preserve keys. + * This function is much faster then $array = $array + $append + * @param array + * @param array + * @return void + */ + private function arrayAppendKeys(array &$array, array $append) + { + foreach ($append as $key => $value) { + $array[$key] = $value; + } + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/Storages/FileStorage.php b/apigen/libs/Nette/Nette/Caching/Storages/FileStorage.php new file mode 100644 index 00000000000..915c6a0cdf0 --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Storages/FileStorage.php @@ -0,0 +1,429 @@ + timestamp) + META_CALLBACKS = 'callbacks'; // array of callbacks (function, args) + + /** additional cache structure */ + const FILE = 'file', + HANDLE = 'handle'; + + + /** @var float probability that the clean() routine is started */ + public static $gcProbability = 0.001; + + /** @var bool */ + public static $useDirectories = TRUE; + + /** @var string */ + private $dir; + + /** @var bool */ + private $useDirs; + + /** @var IJournal */ + private $journal; + + /** @var array */ + private $locks; + + + + public function __construct($dir, IJournal $journal = NULL) + { + $this->dir = realpath($dir); + if ($this->dir === FALSE) { + throw new Nette\DirectoryNotFoundException("Directory '$dir' not found."); + } + + $this->useDirs = (bool) static::$useDirectories; + $this->journal = $journal; + + if (mt_rand() / mt_getrandmax() < static::$gcProbability) { + $this->clean(array()); + } + } + + + + /** + * Read from cache. + * @param string key + * @return mixed|NULL + */ + public function read($key) + { + $meta = $this->readMetaAndLock($this->getCacheFile($key), LOCK_SH); + if ($meta && $this->verify($meta)) { + return $this->readData($meta); // calls fclose() + + } else { + return NULL; + } + } + + + + /** + * Verifies dependencies. + * @param array + * @return bool + */ + private function verify($meta) + { + do { + if (!empty($meta[self::META_DELTA])) { + // meta[file] was added by readMetaAndLock() + if (filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < time()) { + break; + } + touch($meta[self::FILE]); + + } elseif (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < time()) { + break; + } + + if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { + break; + } + + if (!empty($meta[self::META_ITEMS])) { + foreach ($meta[self::META_ITEMS] as $depFile => $time) { + $m = $this->readMetaAndLock($depFile, LOCK_SH); + if ($m[self::META_TIME] !== $time || ($m && !$this->verify($m))) { + break 2; + } + } + } + + return TRUE; + } while (FALSE); + + $this->delete($meta[self::FILE], $meta[self::HANDLE]); // meta[handle] & meta[file] was added by readMetaAndLock() + return FALSE; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + $cacheFile = $this->getCacheFile($key); + if ($this->useDirs && !is_dir($dir = dirname($cacheFile))) { + @mkdir($dir, 0777); // @ - directory may already exist + } + $handle = @fopen($cacheFile, 'r+b'); // @ - file may not exist + if (!$handle) { + $handle = fopen($cacheFile, 'wb'); + if (!$handle) { + return; + } + } + + $this->locks[$key] = $handle; + flock($handle, LOCK_EX); + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + $meta = array( + self::META_TIME => microtime(), + ); + + if (isset($dp[Cache::EXPIRATION])) { + if (empty($dp[Cache::SLIDING])) { + $meta[self::META_EXPIRE] = $dp[Cache::EXPIRATION] + time(); // absolute time + } else { + $meta[self::META_DELTA] = (int) $dp[Cache::EXPIRATION]; // sliding time + } + } + + if (isset($dp[Cache::ITEMS])) { + foreach ((array) $dp[Cache::ITEMS] as $item) { + $depFile = $this->getCacheFile($item); + $m = $this->readMetaAndLock($depFile, LOCK_SH); + $meta[self::META_ITEMS][$depFile] = $m[self::META_TIME]; // may be NULL + unset($m); + } + } + + if (isset($dp[Cache::CALLBACKS])) { + $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; + } + + if (!isset($this->locks[$key])) { + $this->lock($key); + if (!isset($this->locks[$key])) { + return; + } + } + $handle = $this->locks[$key]; + unset($this->locks[$key]); + + $cacheFile = $this->getCacheFile($key); + + if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { + if (!$this->journal) { + throw new Nette\InvalidStateException('CacheJournal has not been provided.'); + } + $this->journal->write($cacheFile, $dp); + } + + ftruncate($handle, 0); + + if (!is_string($data)) { + $data = serialize($data); + $meta[self::META_SERIALIZED] = TRUE; + } + + $head = serialize($meta) . '?>'; + $head = 'delete($cacheFile, $handle); + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + unset($this->locks[$key]); + $this->delete($this->getCacheFile($key)); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + $all = !empty($conds[Cache::ALL]); + $collector = empty($conds); + + // cleaning using file iterator + if ($all || $collector) { + $now = time(); + foreach (Nette\Utils\Finder::find('_*')->from($this->dir)->childFirst() as $entry) { + $path = (string) $entry; + if ($entry->isDir()) { // collector: remove empty dirs + @rmdir($path); // @ - removing dirs is not necessary + continue; + } + if ($all) { + $this->delete($path); + + } else { // collector + $meta = $this->readMetaAndLock($path, LOCK_SH); + if (!$meta) { + continue; + } + + if ((!empty($meta[self::META_DELTA]) && filemtime($meta[self::FILE]) + $meta[self::META_DELTA] < $now) + || (!empty($meta[self::META_EXPIRE]) && $meta[self::META_EXPIRE] < $now) + ) { + $this->delete($path, $meta[self::HANDLE]); + continue; + } + + flock($meta[self::HANDLE], LOCK_UN); + fclose($meta[self::HANDLE]); + } + } + + if ($this->journal) { + $this->journal->clean($conds); + } + return; + } + + // cleaning using journal + if ($this->journal) { + foreach ($this->journal->clean($conds) as $file) { + $this->delete($file); + } + } + } + + + + /** + * Reads cache data from disk. + * @param string file path + * @param int lock mode + * @return array|NULL + */ + protected function readMetaAndLock($file, $lock) + { + $handle = @fopen($file, 'r+b'); // @ - file may not exist + if (!$handle) { + return NULL; + } + + flock($handle, $lock); + + $head = stream_get_contents($handle, self::META_HEADER_LEN); + if ($head && strlen($head) === self::META_HEADER_LEN) { + $size = (int) substr($head, -6); + $meta = stream_get_contents($handle, $size, self::META_HEADER_LEN); + $meta = @unserialize($meta); // intentionally @ + if (is_array($meta)) { + fseek($handle, $size + self::META_HEADER_LEN); // needed by PHP < 5.2.6 + $meta[self::FILE] = $file; + $meta[self::HANDLE] = $handle; + return $meta; + } + } + + flock($handle, LOCK_UN); + fclose($handle); + return NULL; + } + + + + /** + * Reads cache data from disk and closes cache file handle. + * @param array + * @return mixed + */ + protected function readData($meta) + { + $data = stream_get_contents($meta[self::HANDLE]); + flock($meta[self::HANDLE], LOCK_UN); + fclose($meta[self::HANDLE]); + + if (empty($meta[self::META_SERIALIZED])) { + return $data; + } else { + return @unserialize($data); // intentionally @ + } + } + + + + /** + * Returns file name. + * @param string + * @return string + */ + protected function getCacheFile($key) + { + $file = urlencode($key); + if ($this->useDirs && $a = strrpos($file, '%00')) { // %00 = urlencode(Nette\Caching\Cache::NAMESPACE_SEPARATOR) + $file = substr_replace($file, '/_', $a, 3); + } + return $this->dir . '/_' . $file; + } + + + + /** + * Deletes and closes file. + * @param string + * @param resource + * @return void + */ + private static function delete($file, $handle = NULL) + { + if (@unlink($file)) { // @ - file may not already exist + if ($handle) { + flock($handle, LOCK_UN); + fclose($handle); + } + return; + } + + if (!$handle) { + $handle = @fopen($file, 'r+'); // @ - file may not exist + } + if ($handle) { + flock($handle, LOCK_EX); + ftruncate($handle, 0); + flock($handle, LOCK_UN); + fclose($handle); + @unlink($file); // @ - file may not already exist + } + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/Storages/IJournal.php b/apigen/libs/Nette/Nette/Caching/Storages/IJournal.php new file mode 100644 index 00000000000..e5d5e9dadec --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Storages/IJournal.php @@ -0,0 +1,42 @@ +prefix = $prefix; + $this->journal = $journal; + $this->memcache = new \Memcache; + if ($host) { + $this->addServer($host, $port); + } + } + + + + public function addServer($host = 'localhost', $port = 11211, $timeout = 1) + { + Nette\Diagnostics\Debugger::tryError(); + $this->memcache->addServer($host, $port, TRUE, 1, $timeout); + if (Nette\Diagnostics\Debugger::catchError($e)) { + throw new Nette\InvalidStateException('Memcache::addServer(): ' . $e->getMessage(), 0, $e); + } + } + + + + /** + * @return \Memcache + */ + public function getConnection() + { + return $this->memcache; + } + + + + /** + * Read from cache. + * @param string key + * @return mixed|NULL + */ + public function read($key) + { + $key = $this->prefix . $key; + $meta = $this->memcache->get($key); + if (!$meta) { + return NULL; + } + + // meta structure: + // array( + // data => stored data + // delta => relative (sliding) expiration + // callbacks => array of callbacks (function, args) + // ) + + // verify dependencies + if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) { + $this->memcache->delete($key, 0); + return NULL; + } + + if (!empty($meta[self::META_DELTA])) { + $this->memcache->replace($key, $meta, 0, $meta[self::META_DELTA] + time()); + } + + return $meta[self::META_DATA]; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + if (isset($dp[Cache::ITEMS])) { + throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.'); + } + + $key = $this->prefix . $key; + $meta = array( + self::META_DATA => $data, + ); + + $expire = 0; + if (isset($dp[Cache::EXPIRATION])) { + $expire = (int) $dp[Cache::EXPIRATION]; + if (!empty($dp[Cache::SLIDING])) { + $meta[self::META_DELTA] = $expire; // sliding time + } + } + + if (isset($dp[Cache::CALLBACKS])) { + $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS]; + } + + if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) { + if (!$this->journal) { + throw new Nette\InvalidStateException('CacheJournal has not been provided.'); + } + $this->journal->write($key, $dp); + } + + $this->memcache->set($key, $meta, 0, $expire); + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + $this->memcache->delete($this->prefix . $key, 0); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + if (!empty($conds[Cache::ALL])) { + $this->memcache->flush(); + + } elseif ($this->journal) { + foreach ($this->journal->clean($conds) as $entry) { + $this->memcache->delete($entry, 0); + } + } + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/Storages/MemoryStorage.php b/apigen/libs/Nette/Nette/Caching/Storages/MemoryStorage.php new file mode 100644 index 00000000000..34099e95aff --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Storages/MemoryStorage.php @@ -0,0 +1,91 @@ +data[$key]) ? $this->data[$key] : NULL; + } + + + + /** + * Prevents item reading and writing. Lock is released by write() or remove(). + * @param string key + * @return void + */ + public function lock($key) + { + } + + + + /** + * Writes item into the cache. + * @param string key + * @param mixed data + * @param array dependencies + * @return void + */ + public function write($key, $data, array $dp) + { + $this->data[$key] = $data; + } + + + + /** + * Removes item from the cache. + * @param string key + * @return void + */ + public function remove($key) + { + unset($this->data[$key]); + } + + + + /** + * Removes items from the cache by conditions & garbage collector. + * @param array conditions + * @return void + */ + public function clean(array $conds) + { + if (!empty($conds[Nette\Caching\Cache::ALL])) { + $this->data = array(); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Caching/Storages/PhpFileStorage.php b/apigen/libs/Nette/Nette/Caching/Storages/PhpFileStorage.php new file mode 100644 index 00000000000..2a5e4515fcd --- /dev/null +++ b/apigen/libs/Nette/Nette/Caching/Storages/PhpFileStorage.php @@ -0,0 +1,59 @@ + $meta[self::FILE], + 'handle' => $meta[self::HANDLE], + ); + } + + + + /** + * Returns file name. + * @param string + * @return string + */ + protected function getCacheFile($key) + { + return parent::getCacheFile(substr_replace( + $key, + trim(strtr($this->hint, '\\/@', '.._'), '.') . '-', + strpos($key, Nette\Caching\Cache::NAMESPACE_SEPARATOR) + 1, + 0 + )) . '.php'; + } + +} diff --git a/apigen/libs/Nette/Nette/ComponentModel/Component.php b/apigen/libs/Nette/Nette/ComponentModel/Component.php new file mode 100644 index 00000000000..3f6fdd184cc --- /dev/null +++ b/apigen/libs/Nette/Nette/ComponentModel/Component.php @@ -0,0 +1,352 @@ + [obj, depth, path, is_monitored?]] */ + private $monitors = array(); + + + + /** + */ + public function __construct(IContainer $parent = NULL, $name = NULL) + { + if ($parent !== NULL) { + $parent->addComponent($this, $name); + + } elseif (is_string($name)) { + $this->name = $name; + } + } + + + + /** + * Lookup hierarchy for component specified by class or interface name. + * @param string class/interface type + * @param bool throw exception if component doesn't exist? + * @return IComponent + */ + public function lookup($type, $need = TRUE) + { + if (!isset($this->monitors[$type])) { // not monitored or not processed yet + $obj = $this->parent; + $path = self::NAME_SEPARATOR . $this->name; + $depth = 1; + while ($obj !== NULL) { + if ($obj instanceof $type) { + break; + } + $path = self::NAME_SEPARATOR . $obj->getName() . $path; + $depth++; + $obj = $obj->getParent(); // IComponent::getParent() + if ($obj === $this) { + $obj = NULL; // prevent cycling + } + } + + if ($obj) { + $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE); + + } else { + $this->monitors[$type] = array(NULL, NULL, NULL, FALSE); // not found + } + } + + if ($need && $this->monitors[$type][0] === NULL) { + throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'."); + } + + return $this->monitors[$type][0]; + } + + + + /** + * Lookup for component specified by class or interface name. Returns backtrace path. + * A path is the concatenation of component names separated by self::NAME_SEPARATOR. + * @param string class/interface type + * @param bool throw exception if component doesn't exist? + * @return string + */ + public function lookupPath($type, $need = TRUE) + { + $this->lookup($type, $need); + return $this->monitors[$type][2]; + } + + + + /** + * Starts monitoring. + * @param string class/interface type + * @return void + */ + public function monitor($type) + { + if (empty($this->monitors[$type][3])) { + if ($obj = $this->lookup($type, FALSE)) { + $this->attached($obj); + } + $this->monitors[$type][3] = TRUE; // mark as monitored + } + } + + + + /** + * Stops monitoring. + * @param string class/interface type + * @return void + */ + public function unmonitor($type) + { + unset($this->monitors[$type]); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function attached($obj) + { + } + + + + /** + * This method will be called before the component (or component's parent) + * becomes detached from a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function detached($obj) + { + } + + + + /********************* interface IComponent ****************d*g**/ + + + + /** + * @return string + */ + final public function getName() + { + return $this->name; + } + + + + /** + * Returns the container if any. + * @return IContainer|NULL + */ + final public function getParent() + { + return $this->parent; + } + + + + /** + * Sets the parent of this component. This method is managed by containers and should + * not be called by applications + * @param IContainer New parent or null if this component is being removed from a parent + * @param string + * @return Component provides a fluent interface + * @throws Nette\InvalidStateException + * @internal + */ + public function setParent(IContainer $parent = NULL, $name = NULL) + { + if ($parent === NULL && $this->parent === NULL && $name !== NULL) { + $this->name = $name; // just rename + return $this; + + } elseif ($parent === $this->parent && $name === NULL) { + return $this; // nothing to do + } + + // A component cannot be given a parent if it already has a parent. + if ($this->parent !== NULL && $parent !== NULL) { + throw new Nette\InvalidStateException("Component '$this->name' already has a parent."); + } + + // remove from parent? + if ($parent === NULL) { + $this->refreshMonitors(0); + $this->parent = NULL; + + } else { // add to parent + $this->validateParent($parent); + $this->parent = $parent; + if ($name !== NULL) { + $this->name = $name; + } + + $tmp = array(); + $this->refreshMonitors(0, $tmp); + } + return $this; + } + + + + /** + * Is called by a component when it is about to be set new parent. Descendant can + * override this method to disallow a parent change by throwing an Nette\InvalidStateException + * @param IContainer + * @return void + * @throws Nette\InvalidStateException + */ + protected function validateParent(IContainer $parent) + { + } + + + + /** + * Refreshes monitors. + * @param int + * @param array|NULL (array = attaching, NULL = detaching) + * @param array + * @return void + */ + private function refreshMonitors($depth, & $missing = NULL, & $listeners = array()) + { + if ($this instanceof IContainer) { + foreach ($this->getComponents() as $component) { + if ($component instanceof Component) { + $component->refreshMonitors($depth + 1, $missing, $listeners); + } + } + } + + if ($missing === NULL) { // detaching + foreach ($this->monitors as $type => $rec) { + if (isset($rec[1]) && $rec[1] > $depth) { + if ($rec[3]) { // monitored + $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); + $listeners[] = array($this, $rec[0]); + } else { // not monitored, just randomly cached + unset($this->monitors[$type]); + } + } + } + + } else { // attaching + foreach ($this->monitors as $type => $rec) { + if (isset($rec[0])) { // is in cache yet + continue; + + } elseif (!$rec[3]) { // not monitored, just randomly cached + unset($this->monitors[$type]); + + } elseif (isset($missing[$type])) { // known from previous lookup + $this->monitors[$type] = array(NULL, NULL, NULL, TRUE); + + } else { + $this->monitors[$type] = NULL; // forces re-lookup + if ($obj = $this->lookup($type, FALSE)) { + $listeners[] = array($this, $obj); + } else { + $missing[$type] = TRUE; + } + $this->monitors[$type][3] = TRUE; // mark as monitored + } + } + } + + if ($depth === 0) { // call listeners + $method = $missing === NULL ? 'detached' : 'attached'; + foreach ($listeners as $item) { + $item[0]->$method($item[1]); + } + } + } + + + + /********************* cloneable, serializable ****************d*g**/ + + + + /** + * Object cloning. + */ + public function __clone() + { + if ($this->parent === NULL) { + return; + + } elseif ($this->parent instanceof Container) { + $this->parent = $this->parent->_isCloning(); + if ($this->parent === NULL) { // not cloning + $this->refreshMonitors(0); + } + + } else { + $this->parent = NULL; + $this->refreshMonitors(0); + } + } + + + + /** + * Prevents serialization. + */ + final public function __sleep() + { + throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this)); + } + + + + /** + * Prevents unserialization. + */ + final public function __wakeup() + { + throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this)); + } + +} diff --git a/apigen/libs/Nette/Nette/ComponentModel/Container.php b/apigen/libs/Nette/Nette/ComponentModel/Container.php new file mode 100644 index 00000000000..343b396c711 --- /dev/null +++ b/apigen/libs/Nette/Nette/ComponentModel/Container.php @@ -0,0 +1,258 @@ +getName(); + } + + if (is_int($name)) { + $name = (string) $name; + + } elseif (!is_string($name)) { + throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); + + } elseif (!preg_match('#^[a-zA-Z0-9_]+$#', $name)) { + throw new Nette\InvalidArgumentException("Component name must be non-empty alphanumeric string, '$name' given."); + } + + if (isset($this->components[$name])) { + throw new Nette\InvalidStateException("Component with name '$name' already exists."); + } + + // check circular reference + $obj = $this; + do { + if ($obj === $component) { + throw new Nette\InvalidStateException("Circular reference detected while adding component '$name'."); + } + $obj = $obj->getParent(); + } while ($obj !== NULL); + + // user checking + $this->validateChildComponent($component); + + try { + if (isset($this->components[$insertBefore])) { + $tmp = array(); + foreach ($this->components as $k => $v) { + if ($k === $insertBefore) { + $tmp[$name] = $component; + } + $tmp[$k] = $v; + } + $this->components = $tmp; + } else { + $this->components[$name] = $component; + } + $component->setParent($this, $name); + + } catch (\Exception $e) { + unset($this->components[$name]); // undo + throw $e; + } + return $this; + } + + + + /** + * Removes a component from the IContainer. + * @param IComponent + * @return void + */ + public function removeComponent(IComponent $component) + { + $name = $component->getName(); + if (!isset($this->components[$name]) || $this->components[$name] !== $component) { + throw new Nette\InvalidArgumentException("Component named '$name' is not located in this container."); + } + + unset($this->components[$name]); + $component->setParent(NULL); + } + + + + /** + * Returns component specified by name or path. + * @param string + * @param bool throw exception if component doesn't exist? + * @return IComponent|NULL + */ + final public function getComponent($name, $need = TRUE) + { + if (is_int($name)) { + $name = (string) $name; + + } elseif (!is_string($name)) { + throw new Nette\InvalidArgumentException("Component name must be integer or string, " . gettype($name) . " given."); + + } else { + $a = strpos($name, self::NAME_SEPARATOR); + if ($a !== FALSE) { + $ext = (string) substr($name, $a + 1); + $name = substr($name, 0, $a); + } + + if ($name === '') { + throw new Nette\InvalidArgumentException("Component or subcomponent name must not be empty string."); + } + } + + if (!isset($this->components[$name])) { + $component = $this->createComponent($name); + if ($component instanceof IComponent && $component->getParent() === NULL) { + $this->addComponent($component, $name); + } + } + + if (isset($this->components[$name])) { + if (!isset($ext)) { + return $this->components[$name]; + + } elseif ($this->components[$name] instanceof IContainer) { + return $this->components[$name]->getComponent($ext, $need); + + } elseif ($need) { + throw new Nette\InvalidArgumentException("Component with name '$name' is not container and cannot have '$ext' component."); + } + + } elseif ($need) { + throw new Nette\InvalidArgumentException("Component with name '$name' does not exist."); + } + } + + + + /** + * Component factory. Delegates the creation of components to a createComponent method. + * @param string component name + * @return IComponent the created component (optionally) + */ + protected function createComponent($name) + { + $ucname = ucfirst($name); + $method = 'createComponent' . $ucname; + if ($ucname !== $name && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) { + $component = $this->$method($name); + if (!$component instanceof IComponent && !isset($this->components[$name])) { + $class = get_class($this); + throw new Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component."); + } + return $component; + } + } + + + + /** + * Iterates over a components. + * @param bool recursive? + * @param string class types filter + * @return \ArrayIterator + */ + final public function getComponents($deep = FALSE, $filterType = NULL) + { + $iterator = new RecursiveComponentIterator($this->components); + if ($deep) { + $deep = $deep > 0 ? \RecursiveIteratorIterator::SELF_FIRST : \RecursiveIteratorIterator::CHILD_FIRST; + $iterator = new \RecursiveIteratorIterator($iterator, $deep); + } + if ($filterType) { + $iterator = new Nette\Iterators\InstanceFilter($iterator, $filterType); + } + return $iterator; + } + + + + /** + * Descendant can override this method to disallow insert a child by throwing an Nette\InvalidStateException. + * @param IComponent + * @return void + * @throws Nette\InvalidStateException + */ + protected function validateChildComponent(IComponent $child) + { + } + + + + /********************* cloneable, serializable ****************d*g**/ + + + + /** + * Object cloning. + */ + public function __clone() + { + if ($this->components) { + $oldMyself = reset($this->components)->getParent(); + $oldMyself->cloning = $this; + foreach ($this->components as $name => $component) { + $this->components[$name] = clone $component; + } + $oldMyself->cloning = NULL; + } + parent::__clone(); + } + + + + /** + * Is container cloning now? + * @return NULL|IComponent + * @internal + */ + public function _isCloning() + { + return $this->cloning; + } + +} diff --git a/apigen/libs/Nette/Nette/ComponentModel/IComponent.php b/apigen/libs/Nette/Nette/ComponentModel/IComponent.php new file mode 100644 index 00000000000..68f5ed4388e --- /dev/null +++ b/apigen/libs/Nette/Nette/ComponentModel/IComponent.php @@ -0,0 +1,47 @@ +current() instanceof IContainer; + } + + + + /** + * The sub-iterator for the current element. + * @return \RecursiveIterator + */ + public function getChildren() + { + return $this->current()->getComponents(); + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Adapters/IniAdapter.php b/apigen/libs/Nette/Nette/Config/Adapters/IniAdapter.php new file mode 100644 index 00000000000..d8a820baca7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Adapters/IniAdapter.php @@ -0,0 +1,151 @@ +getMessage(), 0, $e); + } + + $data = array(); + foreach ($ini as $secName => $secData) { + if (is_array($secData)) { // is section? + if (substr($secName, -1) === self::RAW_SECTION) { + $secName = substr($secName, 0, -1); + } else { // process key nesting separator (key1.key2.key3) + $tmp = array(); + foreach ($secData as $key => $val) { + $cursor = & $tmp; + $key = str_replace(self::ESCAPED_KEY_SEPARATOR, "\xFF", $key); + foreach (explode(self::KEY_SEPARATOR, $key) as $part) { + $part = str_replace("\xFF", self::KEY_SEPARATOR, $part); + if (!isset($cursor[$part]) || is_array($cursor[$part])) { + $cursor = & $cursor[$part]; + } else { + throw new Nette\InvalidStateException("Invalid key '$key' in section [$secName] in file '$file'."); + } + } + $cursor = $val; + } + $secData = $tmp; + } + + $parts = explode(self::INHERITING_SEPARATOR, $secName); + if (count($parts) > 1) { + $secName = trim($parts[0]); + $secData[Helpers::EXTENDS_KEY] = trim($parts[1]); + } + } + + $cursor = & $data; // nesting separator in section name + foreach (explode(self::KEY_SEPARATOR, $secName) as $part) { + if (!isset($cursor[$part]) || is_array($cursor[$part])) { + $cursor = & $cursor[$part]; + } else { + throw new Nette\InvalidStateException("Invalid section [$secName] in file '$file'."); + } + } + + if (is_array($secData) && is_array($cursor)) { + $secData = Helpers::merge($secData, $cursor); + } + + $cursor = $secData; + } + + return $data; + } + + + + /** + * Generates configuration in INI format. + * @param array + * @return string + */ + public function dump(array $data) + { + $output = array(); + foreach ($data as $name => $secData) { + if (!is_array($secData)) { + $output = array(); + self::build($data, $output, ''); + break; + } + if ($parent = Helpers::takeParent($secData)) { + $output[] = "[$name " . self::INHERITING_SEPARATOR . " $parent]"; + } else { + $output[] = "[$name]"; + } + self::build($secData, $output, ''); + $output[] = ''; + } + return "; generated by Nette\n\n" . implode(PHP_EOL, $output); + } + + + + /** + * Recursive builds INI list. + * @return void + */ + private static function build($input, & $output, $prefix) + { + foreach ($input as $key => $val) { + $key = str_replace(self::KEY_SEPARATOR, self::ESCAPED_KEY_SEPARATOR, $key); + if (is_array($val)) { + self::build($val, $output, $prefix . $key . self::KEY_SEPARATOR); + + } elseif (is_bool($val)) { + $output[] = "$prefix$key = " . ($val ? 'true' : 'false'); + + } elseif (is_numeric($val)) { + $output[] = "$prefix$key = $val"; + + } elseif (is_string($val)) { + $output[] = "$prefix$key = \"$val\""; + + } else { + throw new Nette\InvalidArgumentException("The '$prefix$key' item must be scalar or array, " . gettype($val) ." given."); + } + } + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Adapters/NeonAdapter.php b/apigen/libs/Nette/Nette/Config/Adapters/NeonAdapter.php new file mode 100644 index 00000000000..df23d1b1a37 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Adapters/NeonAdapter.php @@ -0,0 +1,93 @@ +process((array) Neon::decode(file_get_contents($file))); + } + + + + private function process(array $arr) + { + $res = array(); + foreach ($arr as $key => $val) { + if (substr($key, -1) === self::PREVENT_MERGING) { + if (!is_array($val) && $val !== NULL) { + throw new Nette\InvalidStateException("Replacing operator is available only for arrays, item '$key' is not array."); + } + $key = substr($key, 0, -1); + $val[Helpers::EXTENDS_KEY] = Helpers::OVERWRITE; + + } elseif (preg_match('#^(\S+)\s+' . self::INHERITING_SEPARATOR . '\s+(\S+)$#', $key, $matches)) { + if (!is_array($val) && $val !== NULL) { + throw new Nette\InvalidStateException("Inheritance operator is available only for arrays, item '$key' is not array."); + } + list(, $key, $val[Helpers::EXTENDS_KEY]) = $matches; + if (isset($res[$key])) { + throw new Nette\InvalidStateException("Duplicated key '$key'."); + } + } + + if (is_array($val)) { + $val = $this->process($val); + } elseif ($val instanceof Nette\Utils\NeonEntity) { + $val = (object) array('value' => $val->value, 'attributes' => $this->process($val->attributes)); + } + $res[$key] = $val; + } + return $res; + } + + + + /** + * Generates configuration in NEON format. + * @param array + * @return string + */ + public function dump(array $data) + { + $tmp = array(); + foreach ($data as $name => $secData) { + if ($parent = Helpers::takeParent($secData)) { + $name .= ' ' . self::INHERITING_SEPARATOR . ' ' . $parent; + } + $tmp[$name] = $secData; + } + return "# generated by Nette\n\n" . Neon::encode($tmp, Neon::BLOCK); + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Adapters/PhpAdapter.php b/apigen/libs/Nette/Nette/Config/Adapters/PhpAdapter.php new file mode 100644 index 00000000000..a6c5cc8cf9c --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Adapters/PhpAdapter.php @@ -0,0 +1,48 @@ + 1, 'factories' => 1, 'parameters' => 1); + + + + /** + * Add custom configurator extension. + * @return Compiler provides a fluent interface + */ + public function addExtension($name, CompilerExtension $extension) + { + if (isset(self::$reserved[$name])) { + throw new Nette\InvalidArgumentException("Name '$name' is reserved."); + } + $this->extensions[$name] = $extension->setCompiler($this, $name); + return $this; + } + + + + /** + * @return array + */ + public function getExtensions() + { + return $this->extensions; + } + + + + /** + * @return Nette\DI\ContainerBuilder + */ + public function getContainerBuilder() + { + return $this->container; + } + + + + /** + * Returns configuration without expanded parameters. + * @return array + */ + public function getConfig() + { + return $this->config; + } + + + + /** + * @return string + */ + public function compile(array $config, $className, $parentName) + { + $this->config = $config; + $this->container = new Nette\DI\ContainerBuilder; + $this->processParameters(); + $this->processExtensions(); + $this->processServices(); + return $this->generateCode($className, $parentName); + } + + + + public function processParameters() + { + if (isset($this->config['parameters'])) { + $this->container->parameters = $this->config['parameters']; + } + } + + + + public function processExtensions() + { + for ($i = 0; $slice = array_slice($this->extensions, $i, 1); $i++) { + reset($slice)->loadConfiguration(); + } + + if ($extra = array_diff_key($this->config, self::$reserved, $this->extensions)) { + $extra = implode("', '", array_keys($extra)); + throw new Nette\InvalidStateException("Found sections '$extra' in configuration, but corresponding extensions are missing."); + } + } + + + + public function processServices() + { + $this->parseServices($this->container, $this->config); + + foreach ($this->extensions as $name => $extension) { + $this->container->addDefinition($name) + ->setClass('Nette\DI\NestedAccessor', array('@container', $name)) + ->setAutowired(FALSE); + + if (isset($this->config[$name])) { + $this->parseServices($this->container, $this->config[$name], $name); + } + } + + foreach ($this->container->getDefinitions() as $name => $def) { + $factory = $name . 'Factory'; + if (!$def->shared && !$def->internal && !$this->container->hasDefinition($factory)) { + $this->container->addDefinition($factory) + ->setClass('Nette\Callback', array('@container', Nette\DI\Container::getMethodName($name, FALSE))) + ->setAutowired(FALSE) + ->tags = $def->tags; + } + } + } + + + + public function generateCode($className, $parentName) + { + foreach ($this->extensions as $extension) { + $extension->beforeCompile(); + $this->container->addDependency(Nette\Reflection\ClassType::from($extension)->getFileName()); + } + + $classes[] = $class = $this->container->generateClass($parentName); + $class->setName($className) + ->addMethod('initialize'); + + foreach ($this->extensions as $extension) { + $extension->afterCompile($class); + } + + $defs = $this->container->getDefinitions(); + ksort($defs); + $list = array_keys($defs); + foreach (array_reverse($defs, TRUE) as $name => $def) { + if ($def->class === 'Nette\DI\NestedAccessor' && ($found = preg_grep('#^'.$name.'\.#i', $list))) { + $list = array_diff($list, $found); + $def->class = $className . '_' . preg_replace('#\W+#', '_', $name); + $class->documents = preg_replace("#\S+(?= \\$$name$)#", $def->class, $class->documents); + $classes[] = $accessor = new Nette\Utils\PhpGenerator\ClassType($def->class); + foreach ($found as $item) { + if ($defs[$item]->internal) { + continue; + } + $short = substr($item, strlen($name) + 1); + $accessor->addDocument($defs[$item]->shared + ? "@property {$defs[$item]->class} \$$short" + : "@method {$defs[$item]->class} create" . ucfirst("$short()")); + } + } + } + + return implode("\n\n\n", $classes); + } + + + + /********************* tools ****************d*g**/ + + + + /** + * Parses section 'services' from configuration file. + * @return void + */ + public static function parseServices(Nette\DI\ContainerBuilder $container, array $config, $namespace = NULL) + { + $services = isset($config['services']) ? $config['services'] : array(); + $factories = isset($config['factories']) ? $config['factories'] : array(); + if ($tmp = array_intersect_key($services, $factories)) { + $tmp = implode("', '", array_keys($tmp)); + throw new Nette\DI\ServiceCreationException("It is not allowed to use services and factories with the same names: '$tmp'."); + } + + $all = $services + $factories; + uasort($all, function($a, $b) { + return strcmp(Helpers::isInheriting($a), Helpers::isInheriting($b)); + }); + + foreach ($all as $name => $def) { + $shared = array_key_exists($name, $services); + $name = ($namespace ? $namespace . '.' : '') . $name; + + if (($parent = Helpers::takeParent($def)) && $parent !== $name) { + $container->removeDefinition($name); + $definition = $container->addDefinition($name); + if ($parent !== Helpers::OVERWRITE) { + foreach ($container->getDefinition($parent) as $k => $v) { + $definition->$k = unserialize(serialize($v)); // deep clone + } + } + } elseif ($container->hasDefinition($name)) { + $definition = $container->getDefinition($name); + if ($definition->shared !== $shared) { + throw new Nette\DI\ServiceCreationException("It is not allowed to use service and factory with the same name '$name'."); + } + } else { + $definition = $container->addDefinition($name); + } + try { + static::parseService($definition, $def, $shared); + } catch (\Exception $e) { + throw new Nette\DI\ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); + } + } + } + + + + /** + * Parses single service from configuration file. + * @return void + */ + public static function parseService(Nette\DI\ServiceDefinition $definition, $config, $shared = TRUE) + { + if ($config === NULL) { + return; + } elseif (!is_array($config)) { + $config = array('class' => NULL, 'factory' => $config); + } + + $known = $shared + ? array('class', 'factory', 'arguments', 'setup', 'autowired', 'run', 'tags') + : array('class', 'factory', 'arguments', 'setup', 'autowired', 'tags', 'internal', 'parameters'); + + if ($error = array_diff(array_keys($config), $known)) { + throw new Nette\InvalidStateException("Unknown key '" . implode("', '", $error) . "' in definition of service."); + } + + $arguments = array(); + if (array_key_exists('arguments', $config)) { + Validators::assertField($config, 'arguments', 'array'); + $arguments = self::filterArguments($config['arguments']); + $definition->setArguments($arguments); + } + + if (array_key_exists('class', $config) || array_key_exists('factory', $config)) { + $definition->class = NULL; + $definition->factory = NULL; + } + + if (array_key_exists('class', $config)) { + Validators::assertField($config, 'class', 'string|stdClass|null'); + if ($config['class'] instanceof \stdClass) { + $definition->setClass($config['class']->value, self::filterArguments($config['class']->attributes)); + } else { + $definition->setClass($config['class'], $arguments); + } + } + + if (array_key_exists('factory', $config)) { + Validators::assertField($config, 'factory', 'callable|stdClass|null'); + if ($config['factory'] instanceof \stdClass) { + $definition->setFactory($config['factory']->value, self::filterArguments($config['factory']->attributes)); + } else { + $definition->setFactory($config['factory'], $arguments); + } + } + + if (isset($config['setup'])) { + if (Helpers::takeParent($config['setup'])) { + $definition->setup = array(); + } + Validators::assertField($config, 'setup', 'list'); + foreach ($config['setup'] as $id => $setup) { + Validators::assert($setup, 'callable|stdClass', "setup item #$id"); + if ($setup instanceof \stdClass) { + Validators::assert($setup->value, 'callable', "setup item #$id"); + $definition->addSetup($setup->value, self::filterArguments($setup->attributes)); + } else { + $definition->addSetup($setup); + } + } + } + + $definition->setShared($shared); + if (isset($config['parameters'])) { + Validators::assertField($config, 'parameters', 'array'); + $definition->setParameters($config['parameters']); + } + + if (isset($config['autowired'])) { + Validators::assertField($config, 'autowired', 'bool'); + $definition->setAutowired($config['autowired']); + } + + if (isset($config['internal'])) { + Validators::assertField($config, 'internal', 'bool'); + $definition->setInternal($config['internal']); + } + + if (isset($config['run'])) { + $config['tags']['run'] = (bool) $config['run']; + } + + if (isset($config['tags'])) { + Validators::assertField($config, 'tags', 'array'); + if (Helpers::takeParent($config['tags'])) { + $definition->tags = array(); + } + foreach ($config['tags'] as $tag => $attrs) { + if (is_int($tag) && is_string($attrs)) { + $definition->addTag($attrs); + } else { + $definition->addTag($tag, $attrs); + } + } + } + } + + + + /** + * Removes ... and replaces entities with Nette\DI\Statement. + * @return array + */ + public static function filterArguments(array $args) + { + foreach ($args as $k => $v) { + if ($v === '...') { + unset($args[$k]); + } elseif ($v instanceof \stdClass && isset($v->value, $v->attributes)) { + $args[$k] = new Nette\DI\Statement($v->value, self::filterArguments($v->attributes)); + } + } + return $args; + } + +} diff --git a/apigen/libs/Nette/Nette/Config/CompilerExtension.php b/apigen/libs/Nette/Nette/Config/CompilerExtension.php new file mode 100644 index 00000000000..5c66e8e4402 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/CompilerExtension.php @@ -0,0 +1,130 @@ +compiler = $compiler; + $this->name = $name; + return $this; + } + + + + /** + * Returns extension configuration. + * @param array default values. + * @param bool perform %parameters% expansion? + * @return array + */ + public function getConfig(array $defaults = NULL, $expand = TRUE) + { + $config = $this->compiler->getConfig(); + $config = isset($config[$this->name]) ? $config[$this->name] : array(); + unset($config['services'], $config['factories']); + $config = Helpers::merge($config, $defaults); + return $expand ? $this->compiler->getContainerBuilder()->expand($config) : $config; + } + + + + /** + * @return Nette\DI\ContainerBuilder + */ + public function getContainerBuilder() + { + return $this->compiler->getContainerBuilder(); + } + + + + /** + * Reads configuration from file. + * @param string file name + * @return array + */ + public function loadFromFile($file) + { + $loader = new Loader; + $res = $loader->load($file); + $container = $this->compiler->getContainerBuilder(); + foreach ($loader->getDependencies() as $file) { + $container->addDependency($file); + } + return $res; + } + + + + /** + * Prepend extension name to identifier or service name. + * @param string + * @return string + */ + public function prefix($id) + { + return substr_replace($id, $this->name . '.', substr($id, 0, 1) === '@' ? 1 : 0, 0); + } + + + + /** + * Processes configuration data. Intended to be overridden by descendant. + * @return void + */ + public function loadConfiguration() + { + } + + + + /** + * Adjusts DI container before is compiled to PHP class. Intended to be overridden by descendant. + * @return void + */ + public function beforeCompile() + { + } + + + + /** + * Adjusts DI container compiled to PHP class. Intended to be overridden by descendant. + * @return void + */ + public function afterCompile(Nette\Utils\PhpGenerator\ClassType $class) + { + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Configurator.php b/apigen/libs/Nette/Nette/Config/Configurator.php new file mode 100644 index 00000000000..bf7272827ee --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Configurator.php @@ -0,0 +1,343 @@ +parameters = $this->getDefaultParameters(); + } + + + + /** + * Set parameter %debugMode%. + * @param bool|string|array + * @return Configurator provides a fluent interface + */ + public function setDebugMode($value = TRUE) + { + $this->parameters['debugMode'] = is_bool($value) ? $value : self::detectDebugMode($value); + $this->parameters['productionMode'] = !$this->parameters['debugMode']; // compatibility + return $this; + } + + + + /** + * @return bool + */ + public function isDebugMode() + { + return !$this->parameters['productionMode']; + } + + + + /** + * Sets path to temporary directory. + * @return Configurator provides a fluent interface + */ + public function setTempDirectory($path) + { + $this->parameters['tempDir'] = $path; + if (($cacheDir = $this->getCacheDirectory()) && !is_dir($cacheDir)) { + mkdir($cacheDir, 0777); + } + return $this; + } + + + + /** + * Adds new parameters. The %params% will be expanded. + * @return Configurator provides a fluent interface + */ + public function addParameters(array $params) + { + $this->parameters = Helpers::merge($params, $this->parameters); + return $this; + } + + + + /** + * @return array + */ + protected function getDefaultParameters() + { + $trace = /*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE); + $debugMode = static::detectDebugMode(); + return array( + 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : NULL, + 'wwwDir' => isset($_SERVER['SCRIPT_FILENAME']) ? dirname($_SERVER['SCRIPT_FILENAME']) : NULL, + 'debugMode' => $debugMode, + 'productionMode' => !$debugMode, + 'environment' => $debugMode ? self::DEVELOPMENT : self::PRODUCTION, + 'consoleMode' => PHP_SAPI === 'cli', + 'container' => array( + 'class' => 'SystemContainer', + 'parent' => 'Nette\DI\Container', + ) + ); + } + + + + /** + * @param string error log directory + * @param string administrator email + * @return void + */ + public function enableDebugger($logDirectory = NULL, $email = NULL) + { + Nette\Diagnostics\Debugger::$strictMode = TRUE; + Nette\Diagnostics\Debugger::enable($this->parameters['productionMode'], $logDirectory, $email); + } + + + + /** + * @return Nette\Loaders\RobotLoader + */ + public function createRobotLoader() + { + if (!($cacheDir = $this->getCacheDirectory())) { + throw new Nette\InvalidStateException("Set path to temporary directory using setTempDirectory()."); + } + $loader = new Nette\Loaders\RobotLoader; + $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage($cacheDir)); + $loader->autoRebuild = !$this->parameters['productionMode']; + return $loader; + } + + + + /** + * Adds configuration file. + * @return Configurator provides a fluent interface + */ + public function addConfig($file, $section = self::AUTO) + { + $this->files[] = array($file, $section === self::AUTO ? $this->parameters['environment'] : $section); + return $this; + } + + + + /** @deprecated */ + public function loadConfig($file, $section = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use addConfig(file, [section])->createContainer() instead.', E_USER_WARNING); + return $this->addConfig($file, $section)->createContainer(); + } + + + + /** + * Returns system DI container. + * @return \SystemContainer + */ + public function createContainer() + { + if ($cacheDir = $this->getCacheDirectory()) { + $cache = new Cache(new Nette\Caching\Storages\PhpFileStorage($cacheDir), 'Nette.Configurator'); + $cacheKey = array($this->parameters, $this->files); + $cached = $cache->load($cacheKey); + if (!$cached) { + $code = $this->buildContainer($dependencies); + $cache->save($cacheKey, $code, array( + Cache::FILES => $dependencies, + )); + $cached = $cache->load($cacheKey); + } + Nette\Utils\LimitedScope::load($cached['file'], TRUE); + + } elseif ($this->files) { + throw new Nette\InvalidStateException("Set path to temporary directory using setTempDirectory()."); + + } else { + Nette\Utils\LimitedScope::evaluate($this->buildContainer()); // back compatibility with Environment + } + + $container = new $this->parameters['container']['class']; + $container->initialize(); + Nette\Environment::setContext($container); // back compatibility + return $container; + } + + + + /** + * Build system container class. + * @return string + */ + protected function buildContainer(& $dependencies = NULL) + { + $loader = $this->createLoader(); + $config = array(); + $code = "files as $tmp) { + list($file, $section) = $tmp; + $config = Helpers::merge($loader->load($file, $section), $config); + $code .= "// source: $file $section\n"; + } + $code .= "\n"; + + $this->checkCompatibility($config); + + if (!isset($config['parameters'])) { + $config['parameters'] = array(); + } + $config['parameters'] = Helpers::merge($config['parameters'], $this->parameters); + + $compiler = $this->createCompiler(); + $this->onCompile($this, $compiler); + + $code .= $compiler->compile( + $config, + $this->parameters['container']['class'], + $config['parameters']['container']['parent'] + ); + $dependencies = array_merge($loader->getDependencies(), $this->isDebugMode() ? $compiler->getContainerBuilder()->getDependencies() : array()); + return $code; + } + + + + protected function checkCompatibility(array $config) + { + foreach (array('service' => 'services', 'variable' => 'parameters', 'variables' => 'parameters', 'mode' => 'parameters', 'const' => 'constants') as $old => $new) { + if (isset($config[$old])) { + throw new Nette\DeprecatedException("Section '$old' in configuration file is deprecated; use '$new' instead."); + } + } + if (isset($config['services'])) { + foreach ($config['services'] as $key => $def) { + foreach (array('option' => 'arguments', 'methods' => 'setup') as $old => $new) { + if (is_array($def) && isset($def[$old])) { + throw new Nette\DeprecatedException("Section '$old' in service definition is deprecated; refactor it into '$new'."); + } + } + } + } + } + + + + /** + * @return Compiler + */ + protected function createCompiler() + { + $compiler = new Compiler; + $compiler->addExtension('php', new Extensions\PhpExtension) + ->addExtension('constants', new Extensions\ConstantsExtension) + ->addExtension('nette', new Extensions\NetteExtension); + return $compiler; + } + + + + /** + * @return Loader + */ + protected function createLoader() + { + return new Loader; + } + + + + protected function getCacheDirectory() + { + return empty($this->parameters['tempDir']) ? NULL : $this->parameters['tempDir'] . '/cache'; + } + + + + /********************* tools ****************d*g**/ + + + + /** + * Detects debug mode by IP address. + * @param string|array IP addresses or computer names whitelist detection + * @return bool + */ + public static function detectDebugMode($list = NULL) + { + $list = is_string($list) ? preg_split('#[,\s]+#', $list) : (array) $list; + if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $list[] = '127.0.0.1'; + $list[] = '::1'; + } + return in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE); + } + + + + + /** @deprecated */ + public function setProductionMode($value = TRUE) + { + return $this->setDebugMode(is_bool($value) ? !$value : $value); + } + + + + /** @deprecated */ + public function isProductionMode() + { + return !$this->isDebugMode(); + } + + + + /** @deprecated */ + public static function detectProductionMode($list = NULL) + { + return !static::detectDebugMode($list); + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Extensions/ConstantsExtension.php b/apigen/libs/Nette/Nette/Config/Extensions/ConstantsExtension.php new file mode 100644 index 00000000000..fe954d0b75c --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Extensions/ConstantsExtension.php @@ -0,0 +1,34 @@ +getConfig() as $name => $value) { + $class->methods['initialize']->addBody('define(?, ?);', array($name, $value)); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Extensions/NetteExtension.php b/apigen/libs/Nette/Nette/Config/Extensions/NetteExtension.php new file mode 100644 index 00000000000..d26a5fc9d96 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Extensions/NetteExtension.php @@ -0,0 +1,371 @@ + TRUE, + 'session' => array( + 'iAmUsingBadHost' => NULL, + 'autoStart' => 'smart', // true|false|smart + 'expiration' => NULL, + ), + 'application' => array( + 'debugger' => TRUE, + 'errorPresenter' => NULL, + 'catchExceptions' => '%productionMode%', + ), + 'routing' => array( + 'debugger' => TRUE, + 'routes' => array(), // of [mask => action] + ), + 'security' => array( + 'debugger' => TRUE, + 'frames' => 'SAMEORIGIN', // X-Frame-Options + 'users' => array(), // of [user => password] + 'roles' => array(), // of [role => parents] + 'resources' => array(), // of [resource => parents] + ), + 'mailer' => array( + 'smtp' => FALSE, + ), + 'database' => array(), // of [name => dsn, user, password, debugger, explain, autowired, reflection] + 'forms' => array( + 'messages' => array(), + ), + 'container' => array( + 'debugger' => FALSE, + ), + 'debugger' => array( + 'email' => NULL, + 'editor' => NULL, + 'browser' => NULL, + 'strictMode' => NULL, + 'bar' => array(), // of class name + 'blueScreen' => array(), // of callback + ), + ); + + public $databaseDefaults = array( + 'dsn' => NULL, + 'user' => NULL, + 'password' => NULL, + 'options' => NULL, + 'debugger' => TRUE, + 'explain' => TRUE, + 'reflection' => 'Nette\Database\Reflection\DiscoveredReflection', + ); + + + + public function loadConfiguration() + { + $container = $this->getContainerBuilder(); + $config = $this->getConfig($this->defaults); + + + // cache + $container->addDefinition($this->prefix('cacheJournal')) + ->setClass('Nette\Caching\Storages\FileJournal', array('%tempDir%')); + + $container->addDefinition('cacheStorage') // no namespace for back compatibility + ->setClass('Nette\Caching\Storages\FileStorage', array('%tempDir%/cache')); + + $container->addDefinition($this->prefix('templateCacheStorage')) + ->setClass('Nette\Caching\Storages\PhpFileStorage', array('%tempDir%/cache')) + ->setAutowired(FALSE); + + $container->addDefinition($this->prefix('cache')) + ->setClass('Nette\Caching\Cache', array(1 => '%namespace%')) + ->setParameters(array('namespace' => NULL)); + + + // http + $container->addDefinition($this->prefix('httpRequestFactory')) + ->setClass('Nette\Http\RequestFactory') + ->addSetup('setEncoding', array('UTF-8')) + ->setInternal(TRUE); + + $container->addDefinition('httpRequest') // no namespace for back compatibility + ->setClass('Nette\Http\Request') + ->setFactory('@Nette\Http\RequestFactory::createHttpRequest'); + + $container->addDefinition('httpResponse') // no namespace for back compatibility + ->setClass('Nette\Http\Response'); + + $container->addDefinition($this->prefix('httpContext')) + ->setClass('Nette\Http\Context'); + + + // session + $session = $container->addDefinition('session') // no namespace for back compatibility + ->setClass('Nette\Http\Session'); + + if (isset($config['session']['expiration'])) { + $session->addSetup('setExpiration', array($config['session']['expiration'])); + } + if (isset($config['session']['iAmUsingBadHost'])) { + $session->addSetup('Nette\Framework::$iAmUsingBadHost = ?;', array((bool) $config['session']['iAmUsingBadHost'])); + } + unset($config['session']['expiration'], $config['session']['autoStart'], $config['session']['iAmUsingBadHost']); + if (!empty($config['session'])) { + $session->addSetup('setOptions', array($config['session'])); + } + + + // security + $container->addDefinition($this->prefix('userStorage')) + ->setClass('Nette\Http\UserStorage'); + + $user = $container->addDefinition('user') // no namespace for back compatibility + ->setClass('Nette\Security\User'); + + if (!$container->parameters['productionMode'] && $config['security']['debugger']) { + $user->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array( + new Nette\DI\Statement('Nette\Security\Diagnostics\UserPanel') + )); + } + + if ($config['security']['users']) { + $container->addDefinition($this->prefix('authenticator')) + ->setClass('Nette\Security\SimpleAuthenticator', array($config['security']['users'])); + } + + if ($config['security']['roles'] || $config['security']['resources']) { + $authorizator = $container->addDefinition($this->prefix('authorizator')) + ->setClass('Nette\Security\Permission'); + foreach ($config['security']['roles'] as $role => $parents) { + $authorizator->addSetup('addRole', array($role, $parents)); + } + foreach ($config['security']['resources'] as $resource => $parents) { + $authorizator->addSetup('addResource', array($resource, $parents)); + } + } + + + // application + $application = $container->addDefinition('application') // no namespace for back compatibility + ->setClass('Nette\Application\Application') + ->addSetup('$catchExceptions', $config['application']['catchExceptions']) + ->addSetup('$errorPresenter', $config['application']['errorPresenter']); + + if ($config['application']['debugger']) { + $application->addSetup('Nette\Application\Diagnostics\RoutingPanel::initializePanel'); + } + + $container->addDefinition($this->prefix('presenterFactory')) + ->setClass('Nette\Application\PresenterFactory', array( + isset($container->parameters['appDir']) ? $container->parameters['appDir'] : NULL + )); + + + // routing + $router = $container->addDefinition('router') // no namespace for back compatibility + ->setClass('Nette\Application\Routers\RouteList'); + + foreach ($config['routing']['routes'] as $mask => $action) { + $router->addSetup('$service[] = new Nette\Application\Routers\Route(?, ?);', array($mask, $action)); + } + + if (!$container->parameters['productionMode'] && $config['routing']['debugger']) { + $application->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array( + new Nette\DI\Statement('Nette\Application\Diagnostics\RoutingPanel') + )); + } + + + // mailer + if (empty($config['mailer']['smtp'])) { + $container->addDefinition($this->prefix('mailer')) + ->setClass('Nette\Mail\SendmailMailer'); + } else { + $container->addDefinition($this->prefix('mailer')) + ->setClass('Nette\Mail\SmtpMailer', array($config['mailer'])); + } + + $container->addDefinition($this->prefix('mail')) + ->setClass('Nette\Mail\Message') + ->addSetup('setMailer') + ->setShared(FALSE); + + + // forms + $container->addDefinition($this->prefix('basicForm')) + ->setClass('Nette\Forms\Form') + ->setShared(FALSE); + + + // templating + $latte = $container->addDefinition($this->prefix('latte')) + ->setClass('Nette\Latte\Engine') + ->setShared(FALSE); + + if (empty($config['xhtml'])) { + $latte->addSetup('$service->getCompiler()->defaultContentType = ?', Nette\Latte\Compiler::CONTENT_HTML); + } + + $container->addDefinition($this->prefix('template')) + ->setClass('Nette\Templating\FileTemplate') + ->addSetup('registerFilter', array($latte)) + ->addSetup('registerHelperLoader', array('Nette\Templating\Helpers::loader')) + ->setShared(FALSE); + + + // database + $container->addDefinition($this->prefix('database')) + ->setClass('Nette\DI\NestedAccessor', array('@container', $this->prefix('database'))); + + if (isset($config['database']['dsn'])) { + $config['database'] = array('default' => $config['database']); + } + + $autowired = TRUE; + foreach ((array) $config['database'] as $name => $info) { + if (!is_array($info)) { + continue; + } + $info += $this->databaseDefaults + array('autowired' => $autowired); + $autowired = FALSE; + + foreach ((array) $info['options'] as $key => $value) { + unset($info['options'][$key]); + $info['options'][constant($key)] = $value; + } + + $connection = $container->addDefinition($this->prefix("database.$name")) + ->setClass('Nette\Database\Connection', array($info['dsn'], $info['user'], $info['password'], $info['options'])) + ->setAutowired($info['autowired']) + ->addSetup('setCacheStorage') + ->addSetup('Nette\Diagnostics\Debugger::$blueScreen->addPanel(?)', array( + 'Nette\Database\Diagnostics\ConnectionPanel::renderException' + )); + + if ($info['reflection']) { + $connection->addSetup('setDatabaseReflection', is_string($info['reflection']) + ? array(new Nette\DI\Statement(preg_match('#^[a-z]+$#', $info['reflection']) ? 'Nette\Database\Reflection\\' . ucfirst($info['reflection']) . 'Reflection' : $info['reflection'])) + : Nette\Config\Compiler::filterArguments(array($info['reflection'])) + ); + } + + if (!$container->parameters['productionMode'] && $info['debugger']) { + $panel = $container->addDefinition($this->prefix("database.{$name}ConnectionPanel")) + ->setClass('Nette\Database\Diagnostics\ConnectionPanel') + ->setAutowired(FALSE) + ->addSetup('$explain', !empty($info['explain'])) + ->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array('@self')); + + $connection->addSetup('$service->onQuery[] = ?', array(array($panel, 'logQuery'))); + } + } + } + + + + public function afterCompile(Nette\Utils\PhpGenerator\ClassType $class) + { + $initialize = $class->methods['initialize']; + $container = $this->getContainerBuilder(); + $config = $this->getConfig($this->defaults); + + // debugger + foreach (array('email', 'editor', 'browser', 'strictMode', 'maxLen', 'maxDepth') as $key) { + if (isset($config['debugger'][$key])) { + $initialize->addBody('Nette\Diagnostics\Debugger::$? = ?;', array($key, $config['debugger'][$key])); + } + } + + if (!$container->parameters['productionMode']) { + if ($config['container']['debugger']) { + $config['debugger']['bar'][] = 'Nette\DI\Diagnostics\ContainerPanel'; + } + + foreach ((array) $config['debugger']['bar'] as $item) { + $initialize->addBody($container->formatPhp( + 'Nette\Diagnostics\Debugger::$bar->addPanel(?);', + Nette\Config\Compiler::filterArguments(array(is_string($item) ? new Nette\DI\Statement($item) : $item)) + )); + } + + foreach ((array) $config['debugger']['blueScreen'] as $item) { + $initialize->addBody($container->formatPhp( + 'Nette\Diagnostics\Debugger::$blueScreen->addPanel(?);', + Nette\Config\Compiler::filterArguments(array($item)) + )); + } + } + + if (!empty($container->parameters['tempDir'])) { + $initialize->addBody($this->checkTempDir($container->expand('%tempDir%/cache'))); + } + + foreach ((array) $config['forms']['messages'] as $name => $text) { + $initialize->addBody('Nette\Forms\Rules::$defaultMessages[Nette\Forms\Form::?] = ?;', array($name, $text)); + } + + if ($config['session']['autoStart'] === 'smart') { + $initialize->addBody('$this->session->exists() && $this->session->start();'); + } elseif ($config['session']['autoStart']) { + $initialize->addBody('$this->session->start();'); + } + + if (empty($config['xhtml'])) { + $initialize->addBody('Nette\Utils\Html::$xhtml = ?;', array((bool) $config['xhtml'])); + } + + if (isset($config['security']['frames']) && $config['security']['frames'] !== TRUE) { + $frames = $config['security']['frames']; + if ($frames === FALSE) { + $frames = 'DENY'; + } elseif (preg_match('#^https?:#', $frames)) { + $frames = "ALLOW-FROM $frames"; + } + $initialize->addBody('header(?);', array("X-Frame-Options: $frames")); + } + + foreach ($container->findByTag('run') as $name => $on) { + if ($on) { + $initialize->addBody('$this->getService(?);', array($name)); + } + } + } + + + + private function checkTempDir($dir) + { + // checks whether directory is writable + $uniq = uniqid('_', TRUE); + if (!@mkdir("$dir/$uniq", 0777)) { // @ - is escalated to exception + throw new Nette\InvalidStateException("Unable to write to directory '$dir'. Make this directory writable."); + } + + // tests subdirectory mode + $useDirs = @file_put_contents("$dir/$uniq/_", '') !== FALSE; // @ - error is expected + @unlink("$dir/$uniq/_"); + @rmdir("$dir/$uniq"); // @ - directory may not already exist + + return 'Nette\Caching\Storages\FileStorage::$useDirectories = ' . ($useDirs ? 'TRUE' : 'FALSE') . ";\n"; + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Extensions/PhpExtension.php b/apigen/libs/Nette/Nette/Config/Extensions/PhpExtension.php new file mode 100644 index 00000000000..12580b928be --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Extensions/PhpExtension.php @@ -0,0 +1,55 @@ +methods['initialize']; + foreach ($this->getConfig() as $name => $value) { + if (!is_scalar($value)) { + throw new Nette\InvalidStateException("Configuration value for directive '$name' is not scalar."); + + } elseif ($name === 'include_path') { + $initialize->addBody('set_include_path(?);', array(str_replace(';', PATH_SEPARATOR, $value))); + + } elseif ($name === 'ignore_user_abort') { + $initialize->addBody('ignore_user_abort(?);', array($value)); + + } elseif ($name === 'max_execution_time') { + $initialize->addBody('set_time_limit(?);', array($value)); + + } elseif ($name === 'date.timezone') { + $initialize->addBody('date_default_timezone_set(?);', array($value)); + + } elseif (function_exists('ini_set')) { + $initialize->addBody('ini_set(?, ?);', array($name, $value)); + + } elseif (ini_get($name) != $value) { // intentionally == + throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); + } + } + } + +} diff --git a/apigen/libs/Nette/Nette/Config/Helpers.php b/apigen/libs/Nette/Nette/Config/Helpers.php new file mode 100644 index 00000000000..27440594058 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/Helpers.php @@ -0,0 +1,95 @@ + $val) { + if (is_int($key)) { + $right[] = $val; + } else { + if (is_array($val) && isset($val[self::EXTENDS_KEY])) { + if ($val[self::EXTENDS_KEY] === self::OVERWRITE) { + unset($val[self::EXTENDS_KEY]); + } + } elseif (isset($right[$key])) { + $val = static::merge($val, $right[$key]); + } + $right[$key] = $val; + } + } + return $right; + + } elseif ($left === NULL && is_array($right)) { + return $right; + + } else { + return $left; + } + } + + + + /** + * Finds out and removes information about the parent. + * @return mixed + */ + public static function takeParent(& $data) + { + if (is_array($data) && isset($data[self::EXTENDS_KEY])) { + $parent = $data[self::EXTENDS_KEY]; + unset($data[self::EXTENDS_KEY]); + return $parent; + } + } + + + + /** + * @return bool + */ + public static function isOverwriting(& $data) + { + return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] === self::OVERWRITE; + } + + + + /** + * @return bool + */ + public static function isInheriting(& $data) + { + return is_array($data) && isset($data[self::EXTENDS_KEY]) && $data[self::EXTENDS_KEY] !== self::OVERWRITE; + } + +} diff --git a/apigen/libs/Nette/Nette/Config/IAdapter.php b/apigen/libs/Nette/Nette/Config/IAdapter.php new file mode 100644 index 00000000000..0242ca590b7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Config/IAdapter.php @@ -0,0 +1,40 @@ + 'Nette\Config\Adapters\PhpAdapter', + 'ini' => 'Nette\Config\Adapters\IniAdapter', + 'neon' => 'Nette\Config\Adapters\NeonAdapter', + ); + + private $dependencies = array(); + + + + /** + * Reads configuration from file. + * @param string file name + * @param string optional section to load + * @return array + */ + public function load($file, $section = NULL) + { + if (!is_file($file) || !is_readable($file)) { + throw new Nette\FileNotFoundException("File '$file' is missing or is not readable."); + } + $this->dependencies[] = $file = realpath($file); + $data = $this->getAdapter($file)->load($file); + + if ($section) { + if (isset($data[self::INCLUDES_KEY])) { + throw new Nette\InvalidStateException("Section 'includes' must be placed under some top section in file '$file'."); + } + $data = $this->getSection($data, $section, $file); + } + + // include child files + $merged = array(); + if (isset($data[self::INCLUDES_KEY])) { + Validators::assert($data[self::INCLUDES_KEY], 'list', "section 'includes' in file '$file'"); + foreach ($data[self::INCLUDES_KEY] as $include) { + $merged = Helpers::merge($this->load(dirname($file) . '/' . $include), $merged); + } + } + unset($data[self::INCLUDES_KEY]); + + return Helpers::merge($data, $merged); + } + + + + /** + * Save configuration to file. + * @param array + * @param string file + * @return void + */ + public function save($data, $file) + { + if (file_put_contents($file, $this->getAdapter($file)->dump($data)) === FALSE) { + throw new Nette\IOException("Cannot write file '$file'."); + } + } + + + + /** + * Returns configuration files. + * @return array + */ + public function getDependencies() + { + return array_unique($this->dependencies); + } + + + + /** + * Registers adapter for given file extension. + * @param string file extension + * @param string|Nette\Config\IAdapter + * @return Loader provides a fluent interface + */ + public function addAdapter($extension, $adapter) + { + $this->adapters[strtolower($extension)] = $adapter; + return $this; + } + + + + /** @return IAdapter */ + private function getAdapter($file) + { + $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + if (!isset($this->adapters[$extension])) { + throw new Nette\InvalidArgumentException("Unknown file extension '$file'."); + } + return is_object($this->adapters[$extension]) ? $this->adapters[$extension] : new $this->adapters[$extension]; + } + + + + private function getSection(array $data, $key, $file) + { + Validators::assertField($data, $key, 'array|null', "section '%' in file '$file'"); + $item = $data[$key]; + if ($parent = Helpers::takeParent($item)) { + $item = Helpers::merge($item, $this->getSection($data, $parent, $file)); + } + return $item; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/Container.php b/apigen/libs/Nette/Nette/DI/Container.php new file mode 100644 index 00000000000..553e49073f7 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Container.php @@ -0,0 +1,371 @@ +parameters = $params + $this->parameters; + $this->params = &$this->parameters; + } + + + + /** + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + + + /** + * Adds the service or service factory to the container. + * @param string + * @param mixed object, class name or callable + * @param array service meta information + * @return Container provides a fluent interface + */ + public function addService($name, $service, array $meta = NULL) + { + $this->updating(); + if (!is_string($name) || $name === '') { + throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); + } + + if (isset($this->registry[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been registered."); + } + + if (is_object($service) && !$service instanceof \Closure && !$service instanceof Nette\Callback) { + $this->registry[$name] = $service; + $this->meta[$name] = $meta; + return $this; + + } elseif (!is_string($service) || strpos($service, ':') !== FALSE/*5.2* || $service[0] === "\0"*/) { // callable + $service = new Nette\Callback($service); + } + + $this->factories[$name] = array($service); + $this->registry[$name] = & $this->factories[$name][1]; // forces cloning using reference + $this->meta[$name] = $meta; + return $this; + } + + + + /** + * Removes the service from the container. + * @param string + * @return void + */ + public function removeService($name) + { + $this->updating(); + unset($this->registry[$name], $this->factories[$name], $this->meta[$name]); + } + + + + /** + * Gets the service object by name. + * @param string + * @return object + */ + public function getService($name) + { + if (isset($this->registry[$name])) { + return $this->registry[$name]; + + } elseif (isset($this->creating[$name])) { + throw new Nette\InvalidStateException("Circular reference detected for services: " + . implode(', ', array_keys($this->creating)) . "."); + } + + if (isset($this->factories[$name])) { + list($factory) = $this->factories[$name]; + if (is_string($factory)) { + if (!class_exists($factory)) { + throw new Nette\InvalidStateException("Cannot instantiate service, class '$factory' not found."); + } + try { + $this->creating[$name] = TRUE; + $service = new $factory; + } catch (\Exception $e) {} + + } elseif (!$factory->isCallable()) { + throw new Nette\InvalidStateException("Unable to create service '$name', factory '$factory' is not callable."); + + } else { + $this->creating[$name] = TRUE; + try { + $service = $factory/*5.2*->invoke*/($this); + } catch (\Exception $e) {} + } + + } elseif (method_exists($this, $factory = Container::getMethodName($name)) && $this->getReflection()->getMethod($factory)->getName() === $factory) { + $this->creating[$name] = TRUE; + try { + $service = $this->$factory(); + } catch (\Exception $e) {} + + } else { + throw new MissingServiceException("Service '$name' not found."); + } + + unset($this->creating[$name]); + + if (isset($e)) { + throw $e; + + } elseif (!is_object($service)) { + throw new Nette\UnexpectedValueException("Unable to create service '$name', value returned by factory '$factory' is not object."); + } + + return $this->registry[$name] = $service; + } + + + + /** + * Does the service exist? + * @param string service name + * @return bool + */ + public function hasService($name) + { + return isset($this->registry[$name]) + || isset($this->factories[$name]) + || method_exists($this, $method = Container::getMethodName($name)) && $this->getReflection()->getMethod($method)->getName() === $method; + } + + + + /** + * Is the service created? + * @param string service name + * @return bool + */ + public function isCreated($name) + { + if (!$this->hasService($name)) { + throw new MissingServiceException("Service '$name' not found."); + } + return isset($this->registry[$name]); + } + + + + /** + * Resolves service by type. + * @param string class or interface + * @param bool throw exception if service doesn't exist? + * @return object service or NULL + * @throws MissingServiceException + */ + public function getByType($class, $need = TRUE) + { + $lower = ltrim(strtolower($class), '\\'); + if (!isset($this->classes[$lower])) { + if ($need) { + throw new MissingServiceException("Service of type $class not found."); + } + } elseif ($this->classes[$lower] === FALSE) { + throw new MissingServiceException("Multiple services of type $class found."); + } else { + return $this->getService($this->classes[$lower]); + } + } + + + + /** + * Gets the service names of the specified tag. + * @param string + * @return array of [service name => tag attributes] + */ + public function findByTag($tag) + { + $found = array(); + foreach ($this->meta as $name => $meta) { + if (isset($meta[self::TAGS][$tag])) { + $found[$name] = $meta[self::TAGS][$tag]; + } + } + return $found; + } + + + + /********************* autowiring ****************d*g**/ + + + + /** + * Creates new instance using autowiring. + * @param string class + * @param array arguments + * @return object + * @throws Nette\InvalidArgumentException + */ + public function createInstance($class, array $args = array()) + { + $rc = Nette\Reflection\ClassType::from($class); + if (!$rc->isInstantiable()) { + throw new ServiceCreationException("Class $class is not instantiable."); + + } elseif ($constructor = $rc->getConstructor()) { + return $rc->newInstanceArgs(Helpers::autowireArguments($constructor, $args, $this)); + + } elseif ($args) { + throw new ServiceCreationException("Unable to pass arguments, class $class has no constructor."); + } + return new $class; + } + + + + /** + * Calls method using autowiring. + * @param mixed class, object, function, callable + * @param array arguments + * @return mixed + */ + public function callMethod($function, array $args = array()) + { + $callback = new Nette\Callback($function); + return $callback->invokeArgs(Helpers::autowireArguments($callback->toReflection(), $args, $this)); + } + + + + /********************* shortcuts ****************d*g**/ + + + + /** + * Expands %placeholders%. + * @param mixed + * @return mixed + */ + public function expand($s) + { + return Helpers::expand($s, $this->parameters); + } + + + + /** + * Gets the service object, shortcut for getService(). + * @param string + * @return object + */ + public function &__get($name) + { + if (!isset($this->registry[$name])) { + $this->getService($name); + } + return $this->registry[$name]; + } + + + + /** + * Adds the service object. + * @param string + * @param object + * @return void + */ + public function __set($name, $service) + { + $this->updating(); + if (!is_string($name) || $name === '') { + throw new Nette\InvalidArgumentException("Service name must be a non-empty string, " . gettype($name) . " given."); + + } elseif (isset($this->registry[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been registered."); + + } elseif (!is_object($service)) { + throw new Nette\InvalidArgumentException("Service must be a object, " . gettype($service) . " given."); + } + $this->registry[$name] = $service; + } + + + + /** + * Does the service exist? + * @param string + * @return bool + */ + public function __isset($name) + { + return $this->hasService($name); + } + + + + /** + * Removes the service, shortcut for removeService(). + * @return void + */ + public function __unset($name) + { + $this->removeService($name); + } + + + + public static function getMethodName($name, $isService = TRUE) + { + $uname = ucfirst($name); + return ($isService ? 'createService' : 'create') . ($name === $uname ? '__' : '') . str_replace('.', '__', $uname); + } + +} diff --git a/apigen/libs/Nette/Nette/DI/ContainerBuilder.php b/apigen/libs/Nette/Nette/DI/ContainerBuilder.php new file mode 100644 index 00000000000..1ea9f65a46a --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/ContainerBuilder.php @@ -0,0 +1,585 @@ +definitions[$name])) { + throw new Nette\InvalidStateException("Service '$name' has already been added."); + } + return $this->definitions[$name] = new ServiceDefinition; + } + + + + /** + * Removes the specified service definition. + * @param string + * @return void + */ + public function removeDefinition($name) + { + unset($this->definitions[$name]); + } + + + + /** + * Gets the service definition. + * @param string + * @return ServiceDefinition + */ + public function getDefinition($name) + { + if (!isset($this->definitions[$name])) { + throw new MissingServiceException("Service '$name' not found."); + } + return $this->definitions[$name]; + } + + + + /** + * Gets all service definitions. + * @return array + */ + public function getDefinitions() + { + return $this->definitions; + } + + + + /** + * Does the service definition exist? + * @param string + * @return bool + */ + public function hasDefinition($name) + { + return isset($this->definitions[$name]); + } + + + + /********************* class resolving ****************d*g**/ + + + + /** + * Resolves service name by type. + * @param string class or interface + * @return string service name or NULL + * @throws ServiceCreationException + */ + public function getByType($class) + { + $lower = ltrim(strtolower($class), '\\'); + if (!isset($this->classes[$lower])) { + return; + + } elseif (count($this->classes[$lower]) === 1) { + return $this->classes[$lower][0]; + + } else { + throw new ServiceCreationException("Multiple services of type $class found: " . implode(', ', $this->classes[$lower])); + } + } + + + + /** + * Gets the service objects of the specified tag. + * @param string + * @return array of [service name => tag attributes] + */ + public function findByTag($tag) + { + $found = array(); + foreach ($this->definitions as $name => $def) { + if (isset($def->tags[$tag]) && $def->shared) { + $found[$name] = $def->tags[$tag]; + } + } + return $found; + } + + + + /** + * Creates a list of arguments using autowiring. + * @return array + */ + public function autowireArguments($class, $method, array $arguments) + { + $rc = Nette\Reflection\ClassType::from($class); + if (!$rc->hasMethod($method)) { + if (!Nette\Utils\Validators::isList($arguments)) { + throw new ServiceCreationException("Unable to pass specified arguments to $class::$method()."); + } + return $arguments; + } + + $rm = $rc->getMethod($method); + if ($rm->isAbstract() || !$rm->isPublic()) { + throw new ServiceCreationException("$rm is not callable."); + } + $this->addDependency($rm->getFileName()); + return Helpers::autowireArguments($rm, $arguments, $this); + } + + + + /** + * Generates $dependencies, $classes and expands and normalize class names. + * @return array + */ + public function prepareClassList() + { + // complete class-factory pairs; expand classes + foreach ($this->definitions as $name => $def) { + if ($def->class === self::CREATED_SERVICE || ($def->factory && $def->factory->entity === self::CREATED_SERVICE)) { + $def->class = $name; + $def->internal = TRUE; + if ($def->factory && $def->factory->entity === self::CREATED_SERVICE) { + $def->factory->entity = $def->class; + } + unset($this->definitions[$name]); + $this->definitions['_anonymous_' . str_replace('\\', '_', strtolower(trim($name, '\\')))] = $def; + } + + if ($def->class) { + $def->class = $this->expand($def->class); + if (!$def->factory) { + $def->factory = new Statement($def->class); + } + } elseif (!$def->factory) { + throw new ServiceCreationException("Class and factory are missing in service '$name' definition."); + } + } + + // complete classes + $this->classes = FALSE; + foreach ($this->definitions as $name => $def) { + $this->resolveClass($name); + } + + // build auto-wiring list + $this->classes = array(); + foreach ($this->definitions as $name => $def) { + if (!$def->class) { + continue; + } + if (!class_exists($def->class) && !interface_exists($def->class)) { + throw new Nette\InvalidStateException("Class $def->class has not been found."); + } + $def->class = Nette\Reflection\ClassType::from($def->class)->getName(); + if ($def->autowired) { + foreach (class_parents($def->class) + class_implements($def->class) + array($def->class) as $parent) { + $this->classes[strtolower($parent)][] = $name; + } + } + } + + foreach ($this->classes as $class => $foo) { + $this->addDependency(Nette\Reflection\ClassType::from($class)->getFileName()); + } + } + + + + private function resolveClass($name, $recursive = array()) + { + if (isset($recursive[$name])) { + throw new Nette\InvalidArgumentException('Circular reference detected for services: ' . implode(', ', array_keys($recursive)) . '.'); + } + $recursive[$name] = TRUE; + + $def = $this->definitions[$name]; + $factory = $this->normalizeEntity($this->expand($def->factory->entity)); + + if ($def->class) { + return $def->class; + + } elseif (is_array($factory)) { // method calling + if ($service = $this->getServiceName($factory[0])) { + if (Strings::contains($service, '\\')) { // @\Class + throw new ServiceCreationException("Unable resolve class name for service '$name'."); + } + $factory[0] = $this->resolveClass($service, $recursive); + if (!$factory[0]) { + return; + } + } + $factory = new Nette\Callback($factory); + if (!$factory->isCallable()) { + throw new Nette\InvalidStateException("Factory '$factory' is not callable."); + } + try { + $reflection = $factory->toReflection(); + $def->class = preg_replace('#[|\s].*#', '', $reflection->getAnnotation('return')); + if ($def->class && !class_exists($def->class) && $def->class[0] !== '\\' && $reflection instanceof \ReflectionMethod) { + /**/$def->class = $reflection->getDeclaringClass()->getNamespaceName() . '\\' . $def->class;/**/ + } + } catch (\ReflectionException $e) { + } + + } elseif ($service = $this->getServiceName($factory)) { // alias or factory + if (Strings::contains($service, '\\')) { // @\Class + /*5.2* $service = ltrim($service, '\\');*/ + $def->autowired = FALSE; + return $def->class = $service; + } + if ($this->definitions[$service]->shared) { + $def->autowired = FALSE; + } + return $def->class = $this->resolveClass($service, $recursive); + + } else { + return $def->class = $factory; // class name + } + } + + + + /** + * Adds a file to the list of dependencies. + * @return ContainerBuilder provides a fluent interface + */ + public function addDependency($file) + { + $this->dependencies[$file] = TRUE; + return $this; + } + + + + /** + * Returns the list of dependent files. + * @return array + */ + public function getDependencies() + { + unset($this->dependencies[FALSE]); + return array_keys($this->dependencies); + } + + + + /********************* code generator ****************d*g**/ + + + + /** + * Generates PHP class. + * @return Nette\Utils\PhpGenerator\ClassType + */ + public function generateClass($parentClass = 'Nette\DI\Container') + { + unset($this->definitions[self::THIS_CONTAINER]); + $this->addDefinition(self::THIS_CONTAINER)->setClass($parentClass); + + $this->prepareClassList(); + + $class = new Nette\Utils\PhpGenerator\ClassType('Container'); + $class->addExtend($parentClass); + $class->addMethod('__construct') + ->addBody('parent::__construct(?);', array($this->expand($this->parameters))); + + $classes = $class->addProperty('classes', array()); + foreach ($this->classes as $name => $foo) { + try { + $classes->value[$name] = $this->getByType($name); + } catch (ServiceCreationException $e) { + $classes->value[$name] = new PhpLiteral('FALSE, //' . strstr($e->getMessage(), ':')); + } + } + + $definitions = $this->definitions; + ksort($definitions); + + $meta = $class->addProperty('meta', array()); + foreach ($definitions as $name => $def) { + if ($def->shared) { + foreach ($this->expand($def->tags) as $tag => $value) { + $meta->value[$name][Container::TAGS][$tag] = $value; + } + } + } + + foreach ($definitions as $name => $def) { + try { + $type = $def->class ?: 'object'; + $methodName = Container::getMethodName($name, $def->shared); + if (!PhpHelpers::isIdentifier($methodName)) { + throw new ServiceCreationException('Name contains invalid characters.'); + } + if ($def->shared && !$def->internal && PhpHelpers::isIdentifier($name)) { + $class->addDocument("@property $type \$$name"); + } + $method = $class->addMethod($methodName) + ->addDocument("@return $type") + ->setVisibility($def->shared || $def->internal ? 'protected' : 'public') + ->setBody($name === self::THIS_CONTAINER ? 'return $this;' : $this->generateService($name)); + + foreach ($this->expand($def->parameters) as $k => $v) { + $tmp = explode(' ', is_int($k) ? $v : $k); + $param = is_int($k) ? $method->addParameter(end($tmp)) : $method->addParameter(end($tmp), $v); + if (isset($tmp[1])) { + $param->setTypeHint($tmp[0]); + } + } + } catch (\Exception $e) { + throw new ServiceCreationException("Service '$name': " . $e->getMessage(), NULL, $e); + } + } + + return $class; + } + + + + /** + * Generates body of service method. + * @return string + */ + private function generateService($name) + { + $def = $this->definitions[$name]; + $parameters = $this->parameters; + foreach ($this->expand($def->parameters) as $k => $v) { + $v = explode(' ', is_int($k) ? $v : $k); + $parameters[end($v)] = new PhpLiteral('$' . end($v)); + } + + $code = '$service = ' . $this->formatStatement(Helpers::expand($def->factory, $parameters, TRUE)) . ";\n"; + + $entity = $this->normalizeEntity($def->factory->entity); + if ($def->class && $def->class !== $entity && !$this->getServiceName($entity)) { + $code .= PhpHelpers::formatArgs("if (!\$service instanceof $def->class) {\n" + . "\tthrow new Nette\\UnexpectedValueException(?);\n}\n", + array("Unable to create service '$name', value returned by factory is not $def->class type.") + ); + } + + foreach ((array) $def->setup as $setup) { + $setup = Helpers::expand($setup, $parameters, TRUE); + if (is_string($setup->entity) && strpbrk($setup->entity, ':@?') === FALSE) { // auto-prepend @self + $setup->entity = array("@$name", $setup->entity); + } + $code .= $this->formatStatement($setup, $name) . ";\n"; + } + + return $code .= 'return $service;'; + } + + + + /** + * Formats PHP code for class instantiating, function calling or property setting in PHP. + * @return string + * @internal + */ + public function formatStatement(Statement $statement, $self = NULL) + { + $entity = $this->normalizeEntity($statement->entity); + $arguments = $statement->arguments; + + if (is_string($entity) && Strings::contains($entity, '?')) { // PHP literal + return $this->formatPhp($entity, $arguments, $self); + + } elseif ($service = $this->getServiceName($entity)) { // factory calling or service retrieving + if ($this->definitions[$service]->shared) { + if ($arguments) { + throw new ServiceCreationException("Unable to call service '$entity'."); + } + return $this->formatPhp('$this->getService(?)', array($service)); + } + $params = array(); + foreach ($this->definitions[$service]->parameters as $k => $v) { + $params[] = preg_replace('#\w+$#', '\$$0', (is_int($k) ? $v : $k)) . (is_int($k) ? '' : ' = ' . PhpHelpers::dump($v)); + } + $rm = new Nette\Reflection\GlobalFunction(create_function(implode(', ', $params), '')); + $arguments = Helpers::autowireArguments($rm, $arguments, $this); + return $this->formatPhp('$this->?(?*)', array(Container::getMethodName($service, FALSE), $arguments), $self); + + } elseif ($entity === 'not') { // operator + return $this->formatPhp('!?', array($arguments[0])); + + } elseif (is_string($entity)) { // class name + if ($constructor = Nette\Reflection\ClassType::from($entity)->getConstructor()) { + $this->addDependency($constructor->getFileName()); + $arguments = Helpers::autowireArguments($constructor, $arguments, $this); + } elseif ($arguments) { + throw new ServiceCreationException("Unable to pass arguments, class $entity has no constructor."); + } + return $this->formatPhp("new $entity" . ($arguments ? '(?*)' : ''), array($arguments), $self); + + } elseif (!Validators::isList($entity) || count($entity) !== 2) { + throw new Nette\InvalidStateException("Expected class, method or property, " . PhpHelpers::dump($entity) . " given."); + + } elseif ($entity[0] === '') { // globalFunc + return $this->formatPhp("$entity[1](?*)", array($arguments), $self); + + } elseif (Strings::contains($entity[1], '$')) { // property setter + Validators::assert($arguments, 'list:1', "setup arguments for '" . Nette\Callback::create($entity) . "'"); + if ($this->getServiceName($entity[0], $self)) { + return $this->formatPhp('?->? = ?', array($entity[0], substr($entity[1], 1), $arguments[0]), $self); + } else { + return $this->formatPhp($entity[0] . '::$? = ?', array(substr($entity[1], 1), $arguments[0]), $self); + } + + } elseif ($service = $this->getServiceName($entity[0], $self)) { // service method + if ($this->definitions[$service]->class) { + $arguments = $this->autowireArguments($this->definitions[$service]->class, $entity[1], $arguments); + } + return $this->formatPhp('?->?(?*)', array($entity[0], $entity[1], $arguments), $self); + + } else { // static method + $arguments = $this->autowireArguments($entity[0], $entity[1], $arguments); + return $this->formatPhp("$entity[0]::$entity[1](?*)", array($arguments), $self); + } + } + + + + /** + * Formats PHP statement. + * @return string + */ + public function formatPhp($statement, $args, $self = NULL) + { + $that = $this; + array_walk_recursive($args, function(&$val) use ($self, $that) { + list($val) = $that->normalizeEntity(array($val)); + + if ($val instanceof Statement) { + $val = new PhpLiteral($that->formatStatement($val, $self)); + + } elseif ($val === '@' . ContainerBuilder::THIS_CONTAINER) { + $val = new PhpLiteral('$this'); + + } elseif ($service = $that->getServiceName($val, $self)) { + $val = $service === $self ? '$service' : $that->formatStatement(new Statement($val)); + $val = new PhpLiteral($val); + } + }); + return PhpHelpers::formatArgs($statement, $args); + } + + + + /** + * Expands %placeholders% in strings (recursive). + * @param mixed + * @return mixed + */ + public function expand($value) + { + return Helpers::expand($value, $this->parameters, TRUE); + } + + + + /** @internal */ + public function normalizeEntity($entity) + { + if (is_string($entity) && Strings::contains($entity, '::') && !Strings::contains($entity, '?')) { // Class::method -> [Class, method] + $entity = explode('::', $entity); + } + + if (is_array($entity) && $entity[0] instanceof ServiceDefinition) { // [ServiceDefinition, ...] -> [@serviceName, ...] + $tmp = array_keys($this->definitions, $entity[0], TRUE); + $entity[0] = "@$tmp[0]"; + + } elseif ($entity instanceof ServiceDefinition) { // ServiceDefinition -> @serviceName + $tmp = array_keys($this->definitions, $entity, TRUE); + $entity = "@$tmp[0]"; + + } elseif (is_array($entity) && $entity[0] === $this) { // [$this, ...] -> [@container, ...] + $entity[0] = '@' . ContainerBuilder::THIS_CONTAINER; + } + return $entity; // Class, @service, [Class, member], [@service, member], [, globalFunc] + } + + + + /** + * Converts @service or @\Class -> service name and checks its existence. + * @param mixed + * @return string of FALSE, if argument is not service name + */ + public function getServiceName($arg, $self = NULL) + { + if (!is_string($arg) || !preg_match('#^@[\w\\\\.].+$#', $arg)) { + return FALSE; + } + $service = substr($arg, 1); + if ($service === self::CREATED_SERVICE) { + $service = $self; + } + if (Strings::contains($service, '\\')) { + if ($this->classes === FALSE) { // may be disabled by prepareClassList + return $service; + } + $res = $this->getByType($service); + if (!$res) { + throw new ServiceCreationException("Reference to missing service of type $service."); + } + return $res; + } + if (!isset($this->definitions[$service])) { + throw new ServiceCreationException("Reference to missing service '$service'."); + } + return $service; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/Diagnostics/ContainerPanel.php b/apigen/libs/Nette/Nette/DI/Diagnostics/ContainerPanel.php new file mode 100644 index 00000000000..8366d44c3f5 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Diagnostics/ContainerPanel.php @@ -0,0 +1,93 @@ +container = $container; + } + + + + /** + * Renders tab. + * @return string + */ + public function getTab() + { + ob_start(); + require __DIR__ . '/templates/ContainerPanel.tab.phtml'; + return ob_get_clean(); + } + + + + /** + * Renders panel. + * @return string + */ + public function getPanel() + { + $services = $this->getContainerProperty('factories'); + $factories = array(); + foreach (Nette\Reflection\ClassType::from($this->container)->getMethods() as $method) { + if (preg_match('#^create(Service)?(.+)$#', $method->getName(), $m)) { + $name = str_replace('__', '.', strtolower(substr($m[2], 0, 1)) . substr($m[2], 1)); + if ($m[1]) { + $services[$name] = $method->getAnnotation('return'); + } elseif ($method->isPublic()) { + $a = strrpos(".$name", '.'); + $factories[substr($name, 0, $a) . 'create' . ucfirst(substr($name, $a))] = $method->getAnnotation('return'); + } + } + } + ksort($services); + ksort($factories); + $container = $this->container; + $registry = $this->getContainerProperty('registry'); + + ob_start(); + require __DIR__ . '/templates/ContainerPanel.panel.phtml'; + return ob_get_clean(); + } + + + + private function getContainerProperty($name) + { + $prop = Nette\Reflection\ClassType::from('Nette\DI\Container')->getProperty($name); + $prop->setAccessible(TRUE); + return $prop->getValue($this->container); + } + +} diff --git a/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml b/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml new file mode 100644 index 00000000000..c2dd2b93219 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.panel.phtml @@ -0,0 +1,99 @@ + + + +
+

container) ?>

+ +
+

Parameters

+ +
+ container->parameters); ?> +
+ +

Services

+ + + + + + + + + + + + $class): ?> + classes, $name); ?> + + + + + + + + +
NameAutowiredServiceMeta
', $name)) ?>" class=""> + + + + + + + + meta[$name])) { echo Helpers::clickableDump($container->meta[$name], TRUE); } ?>
+ +

Factories

+ + + + + + + + + + $class): ?> + + + + + + +
MethodReturns
', $name)) ?>()
+
+
diff --git a/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml b/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml new file mode 100644 index 00000000000..fc4e3806947 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Diagnostics/templates/ContainerPanel.tab.phtml @@ -0,0 +1,9 @@ + +  diff --git a/apigen/libs/Nette/Nette/DI/Helpers.php b/apigen/libs/Nette/Nette/DI/Helpers.php new file mode 100644 index 00000000000..b53af78c5e7 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Helpers.php @@ -0,0 +1,160 @@ + $val) { + $res[$key] = self::expand($val, $params, $recursive); + } + return $res; + + } elseif ($var instanceof Statement) { + return new Statement(self::expand($var->entity, $params, $recursive), self::expand($var->arguments, $params, $recursive)); + + } elseif (!is_string($var)) { + return $var; + } + + $parts = preg_split('#%([\w.-]*)%#i', $var, -1, PREG_SPLIT_DELIM_CAPTURE); + $res = ''; + foreach ($parts as $n => $part) { + if ($n % 2 === 0) { + $res .= $part; + + } elseif ($part === '') { + $res .= '%'; + + } elseif (isset($recursive[$part])) { + throw new Nette\InvalidArgumentException('Circular reference detected for variables: ' . implode(', ', array_keys($recursive)) . '.'); + + } else { + $val = Nette\Utils\Arrays::get($params, explode('.', $part)); + if ($recursive) { + $val = self::expand($val, $params, (is_array($recursive) ? $recursive : array()) + array($part => 1)); + } + if (strlen($part) + 2 === strlen($var)) { + return $val; + } + if (!is_scalar($val)) { + throw new Nette\InvalidArgumentException("Unable to concatenate non-scalar parameter '$part' into '$var'."); + } + $res .= $val; + } + } + return $res; + } + + + + /** + * Expand counterpart. + * @param mixed + * @return mixed + */ + public static function escape($value) + { + if (is_array($value)) { + array_walk_recursive($value, function(&$val) { + $val = is_string($val) ? str_replace('%', '%%', $val) : $val; + }); + } elseif (is_string($value)) { + $value = str_replace('%', '%%', $value); + } + return $value; + } + + + + /** + * Generates list of arguments using autowiring. + * @param Nette\Reflection\GlobalFunction|Nette\Reflection\Method + * @return array + */ + public static function autowireArguments(\ReflectionFunctionAbstract $method, array $arguments, $container) + { + $optCount = 0; + $num = -1; + $res = array(); + + foreach ($method->getParameters() as $num => $parameter) { + if (array_key_exists($num, $arguments)) { + $res[$num] = $arguments[$num]; + unset($arguments[$num]); + $optCount = 0; + + } elseif (array_key_exists($parameter->getName(), $arguments)) { + $res[$num] = $arguments[$parameter->getName()]; + unset($arguments[$parameter->getName()]); + $optCount = 0; + + } elseif ($class = $parameter->getClassName()) { // has object type hint + $res[$num] = $container->getByType($class, FALSE); + if ($res[$num] === NULL) { + if ($parameter->allowsNull()) { + $optCount++; + } else { + throw new ServiceCreationException("No service of type {$class} found. Make sure the type hint in $method is written correctly and service of this type is registered."); + } + } else { + if ($container instanceof ContainerBuilder) { + $res[$num] = '@' . $res[$num]; + } + $optCount = 0; + } + + } elseif ($parameter->isOptional()) { + // PDO::__construct has optional parameter without default value (and isArray() and allowsNull() returns FALSE) + $res[$num] = $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : NULL; + $optCount++; + + } else { + throw new ServiceCreationException("Parameter $parameter has no type hint, so its value must be specified."); + } + } + + // extra parameters + while (array_key_exists(++$num, $arguments)) { + $res[$num] = $arguments[$num]; + unset($arguments[$num]); + $optCount = 0; + } + if ($arguments) { + throw new ServiceCreationException("Unable to pass specified arguments to $method."); + } + + return $optCount ? array_slice($res, 0, -$optCount) : $res; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/IContainer.php b/apigen/libs/Nette/Nette/DI/IContainer.php new file mode 100644 index 00000000000..ddb39d71b73 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/IContainer.php @@ -0,0 +1,52 @@ +container = $container; + $this->namespace = $namespace . '.'; + $this->parameters = & $container->parameters[$namespace]; + } + + + + /** + * @return object + */ + public function __call($name, $args) + { + if (substr($name, 0, 6) === 'create') { + return call_user_func_array(array( + $this->container, + Container::getMethodName($this->namespace . substr($name, 6), FALSE) + ), $args); + } + throw new Nette\NotSupportedException; + } + + + + /** + * @return object + */ + public function &__get($name) + { + $service = $this->container->getService($this->namespace . $name); + return $service; + } + + + + /** + * @return void + */ + public function __set($name, $service) + { + throw new Nette\NotSupportedException; + } + + + + /** + * @return bool + */ + public function __isset($name) + { + return $this->container->hasService($this->namespace . $name); + } + + + + /** + * @return void + */ + public function __unset($name) + { + throw new Nette\NotSupportedException; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/ServiceDefinition.php b/apigen/libs/Nette/Nette/DI/ServiceDefinition.php new file mode 100644 index 00000000000..eede295b0db --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/ServiceDefinition.php @@ -0,0 +1,134 @@ +class = $class; + if ($args) { + $this->setFactory($class, $args); + } + return $this; + } + + + + public function setFactory($factory, array $args = array()) + { + $this->factory = new Statement($factory, $args); + return $this; + } + + + + public function setArguments(array $args = array()) + { + if ($this->factory) { + $this->factory->arguments = $args; + } else { + $this->setClass($this->class, $args); + } + return $this; + } + + + + public function addSetup($target, $args = NULL) + { + if (!is_array($args)) { + $args = func_get_args(); + array_shift($args); + } + $this->setup[] = new Statement($target, $args); + return $this; + } + + + + public function setParameters(array $params) + { + $this->shared = $this->autowired = FALSE; + $this->parameters = $params; + return $this; + } + + + + public function addTag($tag, $attrs = TRUE) + { + $this->tags[$tag] = $attrs; + return $this; + } + + + + public function setAutowired($on) + { + $this->autowired = $on; + return $this; + } + + + + public function setShared($on) + { + $this->shared = (bool) $on; + $this->autowired = $this->shared ? $this->autowired : FALSE; + return $this; + } + + + + public function setInternal($on) + { + $this->internal = (bool) $on; + return $this; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/Statement.php b/apigen/libs/Nette/Nette/DI/Statement.php new file mode 100644 index 00000000000..c114f9eb6e9 --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/Statement.php @@ -0,0 +1,39 @@ +entity = $entity; + $this->arguments = $arguments; + } + +} diff --git a/apigen/libs/Nette/Nette/DI/exceptions.php b/apigen/libs/Nette/Nette/DI/exceptions.php new file mode 100644 index 00000000000..bc19f91a1db --- /dev/null +++ b/apigen/libs/Nette/Nette/DI/exceptions.php @@ -0,0 +1,32 @@ +dsn = $dsn, $username, $password, $options); + $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Nette\Database\Statement', array($this))); + + $driverClass = $driverClass ?: 'Nette\Database\Drivers\\' . ucfirst(str_replace('sql', 'Sql', $this->getAttribute(PDO::ATTR_DRIVER_NAME))) . 'Driver'; + $this->driver = new $driverClass($this, (array) $options); + $this->preprocessor = new SqlPreprocessor($this); + } + + + + public function getDsn() + { + return $this->dsn; + } + + + + /** @return ISupplementalDriver */ + public function getSupplementalDriver() + { + return $this->driver; + } + + + + /** + * Sets database reflection + * @param IReflection database reflection object + * @return Connection provides a fluent interface + */ + public function setDatabaseReflection(IReflection $databaseReflection) + { + $databaseReflection->setConnection($this); + $this->databaseReflection = $databaseReflection; + return $this; + } + + + + /** @return IReflection */ + public function getDatabaseReflection() + { + if (!$this->databaseReflection) { + $this->setDatabaseReflection(new Reflection\ConventionalReflection); + } + return $this->databaseReflection; + } + + + + /** + * Sets cache storage engine + * @param Nette\Caching\IStorage $storage + * @return Connection provides a fluent interface + */ + public function setCacheStorage(Nette\Caching\IStorage $storage = NULL) + { + $this->cache = $storage ? new Nette\Caching\Cache($storage, 'Nette.Database.' . md5($this->dsn)) : NULL; + return $this; + } + + + + public function getCache() + { + return $this->cache; + } + + + + /** + * Generates and executes SQL query. + * @param string statement + * @param mixed [parameters, ...] + * @return Statement + */ + public function query($statement) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args); + } + + + + /** + * Generates and executes SQL query. + * @param string statement + * @param mixed [parameters, ...] + * @return int number of affected rows + */ + public function exec($statement) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->rowCount(); + } + + + + /** + * @param string statement + * @param array + * @return Statement + */ + public function queryArgs($statement, $params) + { + foreach ($params as $value) { + if (is_array($value) || is_object($value)) { + $need = TRUE; break; + } + } + if (isset($need) && $this->preprocessor !== NULL) { + list($statement, $params) = $this->preprocessor->process($statement, $params); + } + + return $this->prepare($statement)->execute($params); + } + + + + /********************* shortcuts ****************d*g**/ + + + + /** + * Shortcut for query()->fetch() + * @param string statement + * @param mixed [parameters, ...] + * @return Row + */ + public function fetch($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetch(); + } + + + + /** + * Shortcut for query()->fetchColumn() + * @param string statement + * @param mixed [parameters, ...] + * @return mixed + */ + public function fetchColumn($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchColumn(); + } + + + + /** + * Shortcut for query()->fetchPairs() + * @param string statement + * @param mixed [parameters, ...] + * @return array + */ + public function fetchPairs($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchPairs(); + } + + + + /** + * Shortcut for query()->fetchAll() + * @param string statement + * @param mixed [parameters, ...] + * @return array + */ + public function fetchAll($args) + { + $args = func_get_args(); + return $this->queryArgs(array_shift($args), $args)->fetchAll(); + } + + + + /********************* selector ****************d*g**/ + + + + /** + * Creates selector for table. + * @param string + * @return Nette\Database\Table\Selection + */ + public function table($table) + { + return new Table\Selection($table, $this); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * @return Nette\Reflection\ClassType + */ + public /**/static/**/ function getReflection() + { + return new Nette\Reflection\ClassType(/*5.2*$this*//**/get_called_class()/**/); + } + + + + public function __call($name, $args) + { + return ObjectMixin::call($this, $name, $args); + } + + + + public function &__get($name) + { + return ObjectMixin::get($this, $name); + } + + + + public function __set($name, $value) + { + return ObjectMixin::set($this, $name, $value); + } + + + + public function __isset($name) + { + return ObjectMixin::has($this, $name); + } + + + + public function __unset($name) + { + ObjectMixin::remove($this, $name); + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Diagnostics/ConnectionPanel.php b/apigen/libs/Nette/Nette/Database/Diagnostics/ConnectionPanel.php new file mode 100644 index 00000000000..7741cb4faff --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Diagnostics/ConnectionPanel.php @@ -0,0 +1,152 @@ +disabled) { + return; + } + $source = NULL; + foreach (/*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE) as $row) { + if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], NETTE_DIR . DIRECTORY_SEPARATOR) !== 0) { + if (isset($row['function']) && strpos($row['function'], 'call_user_func') === 0) continue; + if (isset($row['class']) && is_subclass_of($row['class'], '\\Nette\\Database\\Connection')) continue; + $source = array($row['file'], (int) $row['line']); + break; + } + } + $this->totalTime += $result->getTime(); + $this->queries[] = array($result->queryString, $params, $result->getTime(), $result->rowCount(), $result->getConnection(), $source); + } + + + + public static function renderException($e) + { + if ($e instanceof \PDOException && isset($e->queryString)) { + return array( + 'tab' => 'SQL', + 'panel' => Helpers::dumpSql($e->queryString), + ); + } + } + + + + public function getTab() + { + return '' + . '' + . count($this->queries) . ' queries' + . ($this->totalTime ? ' / ' . sprintf('%0.1f', $this->totalTime * 1000) . 'ms' : '') + . ''; + } + + + + public function getPanel() + { + $this->disabled = TRUE; + $s = ''; + $h = 'htmlSpecialChars'; + foreach ($this->queries as $i => $query) { + list($sql, $params, $time, $rows, $connection, $source) = $query; + + $explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() + if ($this->explain && preg_match('#\s*\(?\s*SELECT\s#iA', $sql)) { + try { + $cmd = is_string($this->explain) ? $this->explain : 'EXPLAIN'; + $explain = $connection->queryArgs("$cmd $sql", $params)->fetchAll(); + } catch (\PDOException $e) {} + } + + $s .= '' . sprintf('%0.3f', $time * 1000); + if ($explain) { + static $counter; + $counter++; + $s .= "
explain ►"; + } + + $s .= '' . Helpers::dumpSql(self::$maxLength ? Nette\Utils\Strings::truncate($sql, self::$maxLength) : $sql); + if ($explain) { + $s .= ""; + foreach ($explain[0] as $col => $foo) { + $s .= ""; + } + $s .= ""; + foreach ($explain as $row) { + $s .= ""; + foreach ($row as $col) { + $s .= ""; + } + $s .= ""; + } + $s .= "
{$h($col)}
{$h($col)}
"; + } + if ($source) { + $s .= Nette\Diagnostics\Helpers::editorLink($source[0], $source[1])->class('nette-DbConnectionPanel-source'); + } + + $s .= ''; + foreach ($params as $param) { + $s .= Debugger::dump($param, TRUE); + } + + $s .= '' . $rows . ''; + } + + return empty($this->queries) ? '' : + ' +

Queries: ' . count($this->queries) . ($this->totalTime ? ', time: ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms' : '') . '

+
+ + ' . $s . ' +
Time msSQL StatementParamsRows
+
'; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/MsSqlDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/MsSqlDriver.php new file mode 100644 index 00000000000..f7a87bbeecf --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/MsSqlDriver.php @@ -0,0 +1,152 @@ +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://msdn.microsoft.com/en-us/library/ms176027.aspx + return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset support is missing + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; + } + + if ($offset) { + throw new Nette\NotImplementedException('Offset is not implemented.'); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::META; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/MySqlDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/MySqlDriver.php new file mode 100644 index 00000000000..4b604e2129a --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/MySqlDriver.php @@ -0,0 +1,223 @@ + character encoding to set (default is utf8) + * - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html + */ + public function __construct(Nette\Database\Connection $connection, array $options) + { + $this->connection = $connection; + $charset = isset($options['charset']) ? $options['charset'] : 'utf8'; + if ($charset) { + $connection->exec("SET NAMES '$charset'"); + } + if (isset($options['sqlmode'])) { + $connection->exec("SET sql_mode='$options[sqlmode]'"); + } + $connection->exec("SET time_zone='" . date('P') . "'"); + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html + return '`' . str_replace('`', '``', $name) . '`'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_"); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0 || $offset > 0) { + // see http://dev.mysql.com/doc/refman/5.0/en/select.html + $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) + . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + /*$this->connection->query(" + SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = DATABASE() + ");*/ + $tables = array(); + foreach ($this->connection->query('SHOW FULL TABLES') as $row) { + $tables[] = array( + 'name' => $row[0], + 'view' => isset($row[1]) && $row[1] === 'VIEW', + ); + } + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + /*$this->connection->query(" + SELECT * + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = {$this->connection->quote($table)} AND TABLE_SCHEMA = DATABASE() + ");*/ + $columns = array(); + foreach ($this->connection->query('SHOW FULL COLUMNS FROM ' . $this->delimite($table)) as $row) { + $type = explode('(', $row['Type']); + $columns[] = array( + 'name' => $row['Field'], + 'table' => $table, + 'nativetype' => strtoupper($type[0]), + 'size' => isset($type[1]) ? (int) $type[1] : NULL, + 'unsigned' => (bool) strstr($row['Type'], 'unsigned'), + 'nullable' => $row['Null'] === 'YES', + 'default' => $row['Default'], + 'autoincrement' => $row['Extra'] === 'auto_increment', + 'primary' => $row['Key'] === 'PRI', + 'vendor' => (array) $row, + ); + } + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + /*$this->connection->query(" + SELECT * + FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE + WHERE TABLE_NAME = {$this->connection->quote($table)} AND TABLE_SCHEMA = DATABASE() + AND REFERENCED_COLUMN_NAME IS NULL + ");*/ + $indexes = array(); + foreach ($this->connection->query('SHOW INDEX FROM ' . $this->delimite($table)) as $row) { + $indexes[$row['Key_name']]['name'] = $row['Key_name']; + $indexes[$row['Key_name']]['unique'] = !$row['Non_unique']; + $indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY'; + $indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name']; + } + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + $keys = array(); + $query = 'SELECT CONSTRAINT_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM information_schema.KEY_COLUMN_USAGE ' + . 'WHERE TABLE_SCHEMA = DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_NAME = ' . $this->connection->quote($table); + + foreach ($this->connection->query($query) as $id => $row) { + $keys[$id]['name'] = $row['CONSTRAINT_NAME']; // foreign key name + $keys[$id]['local'] = $row['COLUMN_NAME']; // local columns + $keys[$id]['table'] = $row['REFERENCED_TABLE_NAME']; // referenced table + $keys[$id]['foreign'] = $row['REFERENCED_COLUMN_NAME']; // referenced columns + } + + return array_values($keys); + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::META; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/OciDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/OciDriver.php new file mode 100644 index 00000000000..f5a403eee6c --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/OciDriver.php @@ -0,0 +1,165 @@ +connection = $connection; + $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm + return '"' . str_replace('"', '""', $name) . '"'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format($this->fmtDateTime); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + throw new Nette\NotImplementedException; + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($offset > 0) { + // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html + $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' + . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') + . ') WHERE "__rnum" > '. (int) $offset; + + } elseif ($limit >= 0) { + $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + $tables = array(); + foreach ($this->connection->query('SELECT * FROM cat') as $row) { + if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { + $tables[] = array( + 'name' => $row[0], + 'view' => $row[1] === 'VIEW', + ); + } + } + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::META; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/OdbcDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/OdbcDriver.php new file mode 100644 index 00000000000..6c151194729 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/OdbcDriver.php @@ -0,0 +1,151 @@ +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + return '[' . str_replace(array('[', ']'), array('[[', ']]'), $name) . ']'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("#m/d/Y H:i:s#"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + // offset support is missing + if ($limit >= 0) { + $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; + } + + if ($offset) { + throw new Nette\InvalidArgumentException('Offset is not implemented in driver odbc.'); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + throw new NotImplementedException; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotImplementedException; + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::META; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/PgSqlDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/PgSqlDriver.php new file mode 100644 index 00000000000..b9f86440424 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/PgSqlDriver.php @@ -0,0 +1,227 @@ +connection = $connection; + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS + return '"' . str_replace('"', '""', $name) . '"'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format("'Y-m-d H:i:s'"); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = strtr($value, array("'" => "''", '\\' => '\\\\', '%' => '\\\\%', '_' => '\\\\_')); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0) + $sql .= ' LIMIT ' . (int) $limit; + + if ($offset > 0) + $sql .= ' OFFSET ' . (int) $offset; + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + $tables = array(); + foreach ($this->connection->query(" + SELECT + table_name AS name, + table_type = 'VIEW' AS view + FROM + information_schema.tables + WHERE + table_schema = current_schema() + ") as $row) { + $tables[] = (array) $row; + } + + return $tables; + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + $columns = array(); + foreach ($this->connection->query(" + SELECT + c.column_name AS name, + c.table_name AS table, + upper(c.udt_name) AS nativetype, + greatest(c.character_maximum_length, c.numeric_precision) AS size, + FALSE AS unsigned, + c.is_nullable = 'YES' AS nullable, + c.column_default AS default, + coalesce(tc.constraint_type = 'PRIMARY KEY', FALSE) AND strpos(c.column_default, 'nextval') = 1 AS autoincrement, + coalesce(tc.constraint_type = 'PRIMARY KEY', FALSE) AS primary + FROM + information_schema.columns AS c + LEFT JOIN information_schema.constraint_column_usage AS ccu USING(table_catalog, table_schema, table_name, column_name) + LEFT JOIN information_schema.table_constraints AS tc USING(constraint_catalog, constraint_schema, constraint_name) + WHERE + c.table_name = {$this->connection->quote($table)} + AND + c.table_schema = current_schema() + AND + (tc.constraint_type IS NULL OR tc.constraint_type = 'PRIMARY KEY') + ORDER BY + c.ordinal_position + ") as $row) { + $row['vendor'] = array(); + $columns[] = (array) $row; + } + + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + /* There is no information about all indexes in information_schema, so pg catalog must be used */ + $indexes = array(); + foreach ($this->connection->query(" + SELECT + c2.relname AS name, + indisunique AS unique, + indisprimary AS primary, + attname AS column + FROM + pg_class AS c1 + JOIN pg_namespace ON c1.relnamespace = pg_namespace.oid + JOIN pg_index ON c1.oid = indrelid + JOIN pg_class AS c2 ON indexrelid = c2.oid + LEFT JOIN pg_attribute ON c1.oid = attrelid AND attnum = ANY(indkey) + WHERE + nspname = current_schema() + AND + c1.relkind = 'r' + AND + c1.relname = {$this->connection->quote($table)} + ") as $row) { + $indexes[$row['name']]['name'] = $row['name']; + $indexes[$row['name']]['unique'] = $row['unique']; + $indexes[$row['name']]['primary'] = $row['primary']; + $indexes[$row['name']]['columns'][] = $row['column']; + } + + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + return $this->connection->query(" + SELECT tc.table_name AS name, kcu.column_name AS local, ccu.table_name AS table, ccu.column_name AS foreign + FROM information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.constraint_schema = kcu.constraint_schema + JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.constraint_schema = tc.constraint_schema + WHERE + constraint_type = 'FOREIGN KEY' AND + tc.table_name = {$this->connection->quote($table)} AND + tc.constraint_schema = current_schema() + ")->fetchAll(); + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return $item === self::META; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/Sqlite2Driver.php b/apigen/libs/Nette/Nette/Database/Drivers/Sqlite2Driver.php new file mode 100644 index 00000000000..2db3d72e4fe --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/Sqlite2Driver.php @@ -0,0 +1,68 @@ + $value) { + unset($row[$key]); + if ($key[0] === '[' || $key[0] === '"') { + $key = substr($key, 1, -1); + } + $row[$key] = $value; + } + return $row; + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + throw new NotSupportedException; // @see http://www.sqlite.org/foreignkeys.html + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Drivers/SqliteDriver.php b/apigen/libs/Nette/Nette/Database/Drivers/SqliteDriver.php new file mode 100644 index 00000000000..cf80bfa258b --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Drivers/SqliteDriver.php @@ -0,0 +1,232 @@ +connection = $connection; + $this->fmtDateTime = isset($options['formatDateTime']) ? $options['formatDateTime'] : 'U'; + //$connection->exec('PRAGMA foreign_keys = ON'); + } + + + + /********************* SQL ****************d*g**/ + + + + /** + * Delimites identifier for use in a SQL statement. + */ + public function delimite($name) + { + return '[' . strtr($name, '[]', ' ') . ']'; + } + + + + /** + * Formats date-time for use in a SQL statement. + */ + public function formatDateTime(\DateTime $value) + { + return $value->format($this->fmtDateTime); + } + + + + /** + * Encodes string for use in a LIKE statement. + */ + public function formatLike($value, $pos) + { + $value = addcslashes(substr($this->connection->quote($value), 1, -1), '%_\\'); + return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; + } + + + + /** + * Injects LIMIT/OFFSET to the SQL query. + */ + public function applyLimit(&$sql, $limit, $offset) + { + if ($limit >= 0 || $offset > 0) { + $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); + } + } + + + + /** + * Normalizes result row. + */ + public function normalizeRow($row, $statement) + { + return $row; + } + + + + /********************* reflection ****************d*g**/ + + + + /** + * Returns list of tables. + */ + public function getTables() + { + return $this->connection->query(" + SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view') + UNION ALL + SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') + ORDER BY name + ")->fetchAll(); + } + + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns($table) + { + $meta = $this->connection->query(" + SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + UNION ALL + SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->connection->quote($table)} + ")->fetch(); + + $columns = array(); + foreach ($this->connection->query("PRAGMA table_info({$this->delimite($table)})") as $row) { + $column = $row['name']; + $pattern = "/(\"$column\"|\[$column\]|$column)\s+[^,]+\s+PRIMARY\s+KEY\s+AUTOINCREMENT/Ui"; + $type = explode('(', $row['type']); + $columns[] = array( + 'name' => $column, + 'table' => $table, + 'fullname' => "$table.$column", + 'nativetype' => strtoupper($type[0]), + 'size' => isset($type[1]) ? (int) $type[1] : NULL, + 'nullable' => $row['notnull'] == '0', + 'default' => $row['dflt_value'], + 'autoincrement' => (bool) preg_match($pattern, $meta['sql']), + 'primary' => $row['pk'] == '1', + 'vendor' => (array) $row, + ); + } + return $columns; + } + + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes($table) + { + $indexes = array(); + foreach ($this->connection->query("PRAGMA index_list({$this->delimite($table)})") as $row) { + $indexes[$row['name']]['name'] = $row['name']; + $indexes[$row['name']]['unique'] = (bool) $row['unique']; + } + + foreach ($indexes as $index => $values) { + $res = $this->connection->query("PRAGMA index_info({$this->delimite($index)})"); + while ($row = $res->fetch(TRUE)) { + $indexes[$index]['columns'][$row['seqno']] = $row['name']; + } + } + + $columns = $this->getColumns($table); + foreach ($indexes as $index => $values) { + $column = $indexes[$index]['columns'][0]; + $primary = FALSE; + foreach ($columns as $info) { + if ($column == $info['name']) { + $primary = $info['primary']; + break; + } + } + $indexes[$index]['primary'] = (bool) $primary; + } + if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid + foreach ($columns as $column) { + if ($column['vendor']['pk']) { + $indexes[] = array( + 'name' => 'ROWID', + 'unique' => TRUE, + 'primary' => TRUE, + 'columns' => array($column['name']), + ); + break; + } + } + } + + return array_values($indexes); + } + + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys($table) + { + $keys = array(); + foreach ($this->connection->query("PRAGMA foreign_key_list({$this->delimite($table)})") as $row) { + $keys[$row['id']]['name'] = $row['id']; // foreign key name + $keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns + $keys[$row['id']]['table'] = $row['table']; // referenced table + $keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns + $keys[$row['id']]['onDelete'] = $row['on_delete']; + $keys[$row['id']]['onUpdate'] = $row['on_update']; + + if ($keys[$row['id']]['foreign'][0] == NULL) { + $keys[$row['id']]['foreign'] = NULL; + } + } + return array_values($keys); + } + + + + /** + * @return bool + */ + public function isSupported($item) + { + return FALSE; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Helpers.php b/apigen/libs/Nette/Nette/Database/Helpers.php new file mode 100644 index 00000000000..646447e4920 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Helpers.php @@ -0,0 +1,173 @@ + IReflection::FIELD_TEXT, // PostgreSQL arrays + 'BYTEA|BLOB|BIN' => IReflection::FIELD_BINARY, + 'TEXT|CHAR' => IReflection::FIELD_TEXT, + 'YEAR|BYTE|COUNTER|SERIAL|INT|LONG' => IReflection::FIELD_INTEGER, + 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => IReflection::FIELD_FLOAT, + '^TIME$' => IReflection::FIELD_TIME, + 'TIME' => IReflection::FIELD_DATETIME, // DATETIME, TIMESTAMP + 'DATE' => IReflection::FIELD_DATE, + 'BOOL|BIT' => IReflection::FIELD_BOOL, + ); + + + + /** + * Displays complete result set as HTML table for debug purposes. + * @return void + */ + public static function dumpResult(Statement $statement) + { + echo "\n\n\n"; + if (!$statement->columnCount()) { + echo "\t\n\t\t\n\t\t\n\t\n
" . htmlSpecialChars($statement->queryString) . "
Affected rows:", $statement->rowCount(), "
\n"; + return; + } + $i = 0; + foreach ($statement as $row) { + if ($i === 0) { + echo "\n\t\n\t\t#row\n"; + foreach ($row as $col => $foo) { + echo "\t\t" . htmlSpecialChars($col) . "\n"; + } + echo "\t\n\n\n"; + } + echo "\t\n\t\t", $i, "\n"; + foreach ($row as $col) { + //if (is_object($col)) $col = $col->__toString(); + echo "\t\t", htmlSpecialChars($col), "\n"; + } + echo "\t\n"; + $i++; + } + + if ($i === 0) { + echo "\t\n\t\tempty result set\n\t\n\n"; + } else { + echo "\n\n"; + } + } + + + + /** + * Returns syntax highlighted SQL command. + * @param string + * @return string + */ + public static function dumpSql($sql) + { + static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; + static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE'; + + // insert new lines + $sql = " $sql "; + $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); + + // reduce spaces + $sql = preg_replace('#[ \t]{2,}#', " ", $sql); + + $sql = wordwrap($sql, 100); + $sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql); + + // syntax highlight + $sql = htmlSpecialChars($sql); + $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", function($matches) { + if (!empty($matches[1])) // comment + return '' . $matches[1] . ''; + + if (!empty($matches[2])) // error + return '' . $matches[2] . ''; + + if (!empty($matches[3])) // most important keywords + return '' . $matches[3] . ''; + + if (!empty($matches[4])) // other keywords + return '' . $matches[4] . ''; + }, $sql); + + return '
' . trim($sql) . "
\n"; + } + + + + /** + * Heuristic type detection. + * @param string + * @return string + * @internal + */ + public static function detectType($type) + { + static $cache; + if (!isset($cache[$type])) { + $cache[$type] = 'string'; + foreach (self::$typePatterns as $s => $val) { + if (preg_match("#$s#i", $type)) { + return $cache[$type] = $val; + } + } + } + return $cache[$type]; + } + + + + /** + * Import SQL dump from file - extreme fast. + * @return int count of commands + */ + public static function loadFromFile(Connection $connection, $file) + { + @set_time_limit(0); // intentionally @ + + $handle = @fopen($file, 'r'); // intentionally @ + if (!$handle) { + throw new Nette\FileNotFoundException("Cannot open file '$file'."); + } + + $count = 0; + $sql = ''; + while (!feof($handle)) { + $s = fgets($handle); + $sql .= $s; + if (substr(rtrim($s), -1) === ';') { + $connection->exec($sql); // native query without logging + $sql = ''; + $count++; + } + } + if (trim($sql) !== '') { + $connection->exec($sql); + $count++; + } + fclose($handle); + return $count; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/IReflection.php b/apigen/libs/Nette/Nette/Database/IReflection.php new file mode 100644 index 00000000000..1735b478083 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/IReflection.php @@ -0,0 +1,69 @@ +, %2$s for table name + * @param string %1$s stands for key used after ->, %2$s for table name + */ + public function __construct($primary = 'id', $foreign = '%s_id', $table = '%s') + { + $this->primary = $primary; + $this->foreign = $foreign; + $this->table = $table; + } + + + + public function getPrimary($table) + { + return sprintf($this->primary, $this->getColumnFromTable($table)); + } + + + + public function getHasManyReference($table, $key) + { + $table = $this->getColumnFromTable($table); + return array( + sprintf($this->table, $key, $table), + sprintf($this->foreign, $table, $key), + ); + } + + + + public function getBelongsToReference($table, $key) + { + $table = $this->getColumnFromTable($table); + return array( + sprintf($this->table, $key, $table), + sprintf($this->foreign, $key, $table), + ); + } + + + + public function setConnection(Nette\Database\Connection $connection) + {} + + + + protected function getColumnFromTable($name) + { + if ($this->table !== '%s' && preg_match('(^' . str_replace('%s', '(.*)', preg_quote($this->table)) . '$)', $name, $match)) { + return $match[1]; + } + + return $name; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Reflection/DiscoveredReflection.php b/apigen/libs/Nette/Nette/Database/Reflection/DiscoveredReflection.php new file mode 100644 index 00000000000..aaabbd72be0 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Reflection/DiscoveredReflection.php @@ -0,0 +1,194 @@ +cacheStorage = $storage; + } + + + + public function setConnection(Nette\Database\Connection $connection) + { + $this->connection = $connection; + if ($this->cacheStorage) { + $this->cache = new Nette\Caching\Cache($this->cacheStorage, 'Nette.Database.' . md5($connection->getDsn())); + $this->structure = $this->cache->load('structure') ?: $this->structure; + } + } + + + + public function __destruct() + { + if ($this->cache) { + $this->cache->save('structure', $this->structure); + } + } + + + + public function getPrimary($table) + { + $primary = & $this->structure['primary'][strtolower($table)]; + if (isset($primary)) { + return empty($primary) ? NULL : $primary; + } + + $columns = $this->connection->getSupplementalDriver()->getColumns($table); + $primaryCount = 0; + foreach ($columns as $column) { + if ($column['primary']) { + $primary = $column['name']; + $primaryCount++; + } + } + + if ($primaryCount !== 1) { + $primary = ''; + return NULL; + } + + return $primary; + } + + + + public function getHasManyReference($table, $key, $refresh = TRUE) + { + $table = strtolower($table); + $reference = & $this->structure['hasMany']; + if (!empty($reference[$table])) { + $candidates = array(); + $subStringCandidatesCount = 0; + foreach ($reference[$table] as $targetPair) { + list($targetColumn, $targetTable) = $targetPair; + if (stripos($targetTable, $key) !== FALSE) { + $candidates[] = array($targetTable, $targetColumn); + if (stripos($targetColumn, $table) !== FALSE) { + $subStringCandidatesCount++; + $candidate = array($targetTable, $targetColumn); + if ($targetTable === $key) { + $candidates = array($candidate); + break; + } + } + } + } + + if (count($candidates) === 1) { + return $candidates[0]; + } elseif ($subStringCandidatesCount === 1) { + return $candidate; + } elseif (!empty($candidates)) { + throw new \PDOException('Ambiguous joining column in related call.'); + } + } + + if (!$refresh) { + throw new \PDOException("No reference found for \${$table}->related({$key})."); + } + + $this->reloadAllForeignKeys(); + return $this->getHasManyReference($table, $key, FALSE); + } + + + + public function getBelongsToReference($table, $key, $refresh = TRUE) + { + $table = strtolower($table); + $reference = & $this->structure['belongsTo']; + if (!empty($reference[$table])) { + foreach ($reference[$table] as $column => $targetTable) { + if (stripos($column, $key) !== FALSE) { + return array( + $targetTable, + $column, + ); + } + } + } + + if (!$refresh) { + throw new \PDOException("No reference found for \${$table}->{$key}."); + } + + $this->reloadForeignKeys($table); + return $this->getBelongsToReference($table, $key, FALSE); + } + + + + protected function reloadAllForeignKeys() + { + foreach ($this->connection->getSupplementalDriver()->getTables() as $table) { + if ($table['view'] == FALSE) { + $this->reloadForeignKeys($table['name']); + } + } + + foreach (array_keys($this->structure['hasMany']) as $table) { + uksort($this->structure['hasMany'][$table], function($a, $b) { + return strlen($a) - strlen($b); + }); + } + } + + + + protected function reloadForeignKeys($table) + { + foreach ($this->connection->getSupplementalDriver()->getForeignKeys($table) as $row) { + $this->structure['belongsTo'][$table][$row['local']] = $row['table']; + $this->structure['hasMany'][strtolower($row['table'])][$row['local'] . $table] = array($row['local'], $table); + } + + if (isset($this->structure['belongsTo'][$table])) { + uksort($this->structure['belongsTo'][$table], function($a, $b) { + return strlen($a) - strlen($b); + }); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Row.php b/apigen/libs/Nette/Nette/Database/Row.php new file mode 100644 index 00000000000..aa39e4cacdd --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Row.php @@ -0,0 +1,47 @@ +normalizeRow($this); + } + + + + /** + * Returns a item. + * @param mixed key or index + * @return mixed + */ + public function offsetGet($key) + { + if (is_int($key)) { + $arr = array_values((array) $this); + return $arr[$key]; + } + return $this->$key; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/SqlLiteral.php b/apigen/libs/Nette/Nette/Database/SqlLiteral.php new file mode 100644 index 00000000000..14144f716c7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/SqlLiteral.php @@ -0,0 +1,44 @@ +value = (string) $value; + } + + + + /** + * @return string + */ + public function __toString() + { + return $this->value; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/SqlPreprocessor.php b/apigen/libs/Nette/Nette/Database/SqlPreprocessor.php new file mode 100644 index 00000000000..6e6451d9733 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/SqlPreprocessor.php @@ -0,0 +1,163 @@ +connection = $connection; + $this->driver = $connection->getSupplementalDriver(); + } + + + + /** + * @param string + * @param array + * @return array of [sql, params] + */ + public function process($sql, $params) + { + $this->params = $params; + $this->counter = 0; + $this->remaining = array(); + $this->arrayMode = 'assoc'; + + $sql = Nette\Utils\Strings::replace($sql, '~\'.*?\'|".*?"|\?|\b(?:INSERT|REPLACE|UPDATE)\b~si', array($this, 'callback')); + + while ($this->counter < count($params)) { + $sql .= ' ' . $this->formatValue($params[$this->counter++]); + } + + return array($sql, $this->remaining); + } + + + + /** @internal */ + public function callback($m) + { + $m = $m[0]; + if ($m[0] === "'" || $m[0] === '"') { // string + return $m; + + } elseif ($m === '?') { // placeholder + return $this->formatValue($this->params[$this->counter++]); + + } else { // INSERT, REPLACE, UPDATE + $this->arrayMode = strtoupper($m) === 'UPDATE' ? 'assoc' : 'values'; + return $m; + } + } + + + + private function formatValue($value) + { + if (is_string($value)) { + if (strlen($value) > 20) { + $this->remaining[] = $value; + return '?'; + + } else { + return $this->connection->quote($value); + } + + } elseif (is_int($value)) { + return (string) $value; + + } elseif (is_float($value)) { + return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); + + } elseif (is_bool($value)) { + $this->remaining[] = $value; + return '?'; + + } elseif ($value === NULL) { + return 'NULL'; + + } elseif ($value instanceof Table\ActiveRow) { + return $value->getPrimary(); + + } elseif (is_array($value) || $value instanceof \Traversable) { + $vx = $kx = array(); + + if (isset($value[0])) { // non-associative; value, value, value + foreach ($value as $v) { + $vx[] = $this->formatValue($v); + } + return implode(', ', $vx); + + } elseif ($this->arrayMode === 'values') { // (key, key, ...) VALUES (value, value, ...) + $this->arrayMode = 'multi'; + foreach ($value as $k => $v) { + $kx[] = $this->driver->delimite($k); + $vx[] = $this->formatValue($v); + } + return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; + + } elseif ($this->arrayMode === 'assoc') { // key=value, key=value, ... + foreach ($value as $k => $v) { + $vx[] = $this->driver->delimite($k) . '=' . $this->formatValue($v); + } + return implode(', ', $vx); + + } elseif ($this->arrayMode === 'multi') { // multiple insert (value, value, ...), ... + foreach ($value as $k => $v) { + $vx[] = $this->formatValue($v); + } + return '(' . implode(', ', $vx) . ')'; + } + + } elseif ($value instanceof \DateTime) { + return $this->driver->formatDateTime($value); + + } elseif ($value instanceof SqlLiteral) { + return $value->__toString(); + + } else { + $this->remaining[] = $value; + return '?'; + } + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Statement.php b/apigen/libs/Nette/Nette/Database/Statement.php new file mode 100644 index 00000000000..4ad5ae46c35 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Statement.php @@ -0,0 +1,221 @@ +connection = $connection; + $this->setFetchMode(PDO::FETCH_CLASS, 'Nette\Database\Row', array($this)); + } + + + + /** + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } + + + + /** + * Executes statement. + * @param array + * @return Statement provides a fluent interface + */ + public function execute($params = array()) + { + static $types = array('boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, + 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL); + + foreach ($params as $key => $value) { + $type = gettype($value); + $this->bindValue(is_int($key) ? $key + 1 : $key, $value, isset($types[$type]) ? $types[$type] : PDO::PARAM_STR); + } + + $time = microtime(TRUE); + try { + parent::execute(); + } catch (\PDOException $e) { + $e->queryString = $this->queryString; + throw $e; + } + $this->time = microtime(TRUE) - $time; + $this->connection->__call('onQuery', array($this, $params)); // $this->connection->onQuery() in PHP 5.3 + + return $this; + } + + + + /** + * Fetches into an array where the 1st column is a key and all subsequent columns are values. + * @return array + */ + public function fetchPairs() + { + return $this->fetchAll(PDO::FETCH_KEY_PAIR); // since PHP 5.2.3 + } + + + + /** + * Normalizes result row. + * @param array + * @return array + */ + public function normalizeRow($row) + { + foreach ($this->detectColumnTypes() as $key => $type) { + $value = $row[$key]; + if ($value === NULL || $value === FALSE || $type === IReflection::FIELD_TEXT) { + + } elseif ($type === IReflection::FIELD_INTEGER) { + $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp; + + } elseif ($type === IReflection::FIELD_FLOAT) { + $row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value; + + } elseif ($type === IReflection::FIELD_BOOL) { + $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; + + } elseif ($type === IReflection::FIELD_DATETIME || $type === IReflection::FIELD_DATE || $type === IReflection::FIELD_TIME) { + $row[$key] = new Nette\DateTime($value); + + } + } + + return $this->connection->getSupplementalDriver()->normalizeRow($row, $this); + } + + + + private function detectColumnTypes() + { + if ($this->types === NULL) { + $this->types = array(); + if ($this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::META)) { // workaround for PHP bugs #53782, #54695 + $col = 0; + while ($meta = $this->getColumnMeta($col++)) { + if (isset($meta['native_type'])) { + $this->types[$meta['name']] = Helpers::detectType($meta['native_type']); + } + } + } + } + return $this->types; + } + + + + /** + * @return float + */ + public function getTime() + { + return $this->time; + } + + + + /********************* misc tools ****************d*g**/ + + + + /** + * Displays complete result set as HTML table for debug purposes. + * @return void + */ + public function dump() + { + Helpers::dumpResult($this); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * @return Nette\Reflection\ClassType + */ + public /**/static/**/ function getReflection() + { + return new Nette\Reflection\ClassType(/*5.2*$this*//**/get_called_class()/**/); + } + + + + public function __call($name, $args) + { + return ObjectMixin::call($this, $name, $args); + } + + + + public function &__get($name) + { + return ObjectMixin::get($this, $name); + } + + + + public function __set($name, $value) + { + return ObjectMixin::set($this, $name, $value); + } + + + + public function __isset($name) + { + return ObjectMixin::has($this, $name); + } + + + + public function __unset($name) + { + ObjectMixin::remove($this, $name); + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Table/ActiveRow.php b/apigen/libs/Nette/Nette/Database/Table/ActiveRow.php new file mode 100644 index 00000000000..1e565cc75c4 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Table/ActiveRow.php @@ -0,0 +1,316 @@ +data = $data; + $this->table = $table; + } + + + + /** + * @internal + * @ignore + */ + public function setTable(Selection $table) + { + $this->table = $table; + } + + + + /** + * @internal + * @ignore + */ + public function getTable() + { + return $this->table; + } + + + + public function __toString() + { + try { + return (string) $this->getPrimary(); + } catch (\Exception $e) { + Nette\Diagnostics\Debugger::toStringException($e); + } + } + + + + /** + * @return array + */ + public function toArray() + { + $this->access(NULL); + return $this->data; + } + + + + /** + * Returns primary key value. + * @return mixed + */ + public function getPrimary() + { + if (!isset($this->data[$this->table->getPrimary()])) { + throw new Nette\NotSupportedException("Table {$this->table->getName()} does not have any primary key."); + } + return $this[$this->table->getPrimary()]; + } + + + + /** + * Returns referenced row. + * @param string + * @param string + * @return ActiveRow or NULL if the row does not exist + */ + public function ref($key, $throughColumn = NULL) + { + if (!$throughColumn) { + list($key, $throughColumn) = $this->table->getConnection()->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key); + } + + return $this->getReference($key, $throughColumn); + } + + + + /** + * Returns referencing rows. + * @param string + * @param string + * @return GroupedSelection + */ + public function related($key, $throughColumn = NULL) + { + if (strpos($key, '.') !== FALSE) { + list($key, $throughColumn) = explode('.', $key); + } elseif (!$throughColumn) { + list($key, $throughColumn) = $this->table->getConnection()->getDatabaseReflection()->getHasManyReference($this->table->getName(), $key); + } + + return $this->table->getReferencingTable($key, $throughColumn, $this[$this->table->getPrimary()]); + } + + + + /** + * Updates row. + * @param array or NULL for all modified values + * @return int number of affected rows or FALSE in case of an error + */ + public function update($data = NULL) + { + if ($data === NULL) { + $data = $this->modified; + } + return $this->table->getConnection()->table($this->table->getName()) + ->where($this->table->getPrimary(), $this[$this->table->getPrimary()]) + ->update($data); + } + + + + /** + * Deletes row. + * @return int number of affected rows or FALSE in case of an error + */ + public function delete() + { + return $this->table->getConnection()->table($this->table->getName()) + ->where($this->table->getPrimary(), $this[$this->table->getPrimary()]) + ->delete(); + } + + + + /********************* interface IteratorAggregate ****************d*g**/ + + + + public function getIterator() + { + $this->access(NULL); + return new \ArrayIterator($this->data); + } + + + + /********************* interface ArrayAccess & magic accessors ****************d*g**/ + + + + /** + * Stores value in column. + * @param string column name + * @param string value + * @return void + */ + public function offsetSet($key, $value) + { + $this->__set($key, $value); + } + + + + /** + * Returns value of column. + * @param string column name + * @return string + */ + public function offsetGet($key) + { + return $this->__get($key); + } + + + + /** + * Tests if column exists. + * @param string column name + * @return bool + */ + public function offsetExists($key) + { + return $this->__isset($key); + } + + + + /** + * Removes column from data. + * @param string column name + * @return void + */ + public function offsetUnset($key) + { + $this->__unset($key); + } + + + + public function __set($key, $value) + { + $this->data[$key] = $value; + $this->modified[$key] = $value; + } + + + + public function &__get($key) + { + $this->access($key); + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + + list($table, $column) = $this->table->getConnection()->getDatabaseReflection()->getBelongsToReference($this->table->getName(), $key); + $referenced = $this->getReference($table, $column); + if ($referenced !== FALSE) { + $this->access($key, FALSE); + return $referenced; + } + + $this->access($key, NULL); + throw new Nette\MemberAccessException("Cannot read an undeclared column \"$key\"."); + } + + + + public function __isset($key) + { + $this->access($key); + if (array_key_exists($key, $this->data)) { + return isset($this->data[$key]); + } + $this->access($key, NULL); + return FALSE; + } + + + + public function __unset($key) + { + unset($this->data[$key]); + unset($this->modified[$key]); + } + + + + /** + * @internal + */ + public function access($key, $cache = TRUE) + { + if ($this->table->getConnection()->getCache() && !isset($this->modified[$key]) && $this->table->access($key, $cache)) { + $id = (isset($this->data[$this->table->getPrimary()]) ? $this->data[$this->table->getPrimary()] : $this->data); + $this->data = $this->table[$id]->data; + } + } + + + + protected function getReference($table, $column) + { + if (array_key_exists($column, $this->data)) { + $this->access($column); + + $value = $this->data[$column]; + $value = $value instanceof ActiveRow ? $value->getPrimary() : $value; + + $referenced = $this->table->getReferencedTable($table, $column, !empty($this->modified[$column])); + $referenced = isset($referenced[$value]) ? $referenced[$value] : NULL; // referenced row may not exist + + if (!empty($this->modified[$column])) { // cause saving changed column and prevent regenerating referenced table for $column + $this->modified[$column] = 0; // 0 fails on empty, pass on isset + } + + return $referenced; + } + + return FALSE; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Table/GroupedSelection.php b/apigen/libs/Nette/Nette/Database/Table/GroupedSelection.php new file mode 100644 index 00000000000..7d92eff048c --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Table/GroupedSelection.php @@ -0,0 +1,257 @@ +connection); + $this->refTable = $refTable; + $this->column = $column; + } + + + + /** + * Sets active group + * @internal + * @param int primary key of grouped rows + * @return GroupedSelection + */ + public function setActive($active) + { + $this->active = $active; + return $this; + } + + + + /** @deprecated */ + public function through($column) + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::related("' . $this->name . '", "' . $column . '") instead.', E_USER_WARNING); + $this->column = $column; + $this->delimitedColumn = $this->refTable->connection->getSupplementalDriver()->delimite($this->column); + return $this; + } + + + + public function select($columns) + { + if (!$this->sqlBuilder->getSelect()) { + $this->sqlBuilder->addSelect("$this->name.$this->column"); + } + + return parent::select($columns); + } + + + + public function order($columns) + { + if (!$this->sqlBuilder->getOrder()) { + // improve index utilization + $this->sqlBuilder->addOrder("$this->name.$this->column" . (preg_match('~\\bDESC$~i', $columns) ? ' DESC' : '')); + } + + return parent::order($columns); + } + + + + /********************* aggregations ****************d*g**/ + + + + public function aggregation($function) + { + $aggregation = & $this->getRefTable($refPath)->aggregation[$refPath . $function . $this->sqlBuilder->buildSelectQuery() . json_encode($this->sqlBuilder->getParameters())]; + + if ($aggregation === NULL) { + $aggregation = array(); + + $selection = $this->createSelectionInstance(); + $selection->getSqlBuilder()->importConditions($this->getSqlBuilder()); + $selection->select($function); + $selection->select("$this->name.$this->column"); + $selection->group("$this->name.$this->column"); + + foreach ($selection as $row) { + $aggregation[$row[$this->column]] = $row; + } + } + + if (isset($aggregation[$this->active])) { + foreach ($aggregation[$this->active] as $val) { + return $val; + } + } + } + + + + public function count($column = NULL) + { + $return = parent::count($column); + return isset($return) ? $return : 0; + } + + + + /********************* internal ****************d*g**/ + + + + protected function execute() + { + if ($this->rows !== NULL) { + return; + } + + $hash = md5($this->sqlBuilder->buildSelectQuery() . json_encode($this->sqlBuilder->getParameters())); + + $referencing = & $this->getRefTable($refPath)->referencing[$refPath . $hash]; + $this->rows = & $referencing['rows']; + $this->referenced = & $referencing['refs']; + $this->accessed = & $referencing['accessed']; + $refData = & $referencing['data']; + + if ($refData === NULL) { + $limit = $this->sqlBuilder->getLimit(); + $rows = count($this->refTable->rows); + if ($limit && $rows > 1) { + $this->sqlBuilder->setLimit(NULL, NULL); + } + parent::execute(); + $this->sqlBuilder->setLimit($limit, NULL); + $refData = array(); + $offset = array(); + foreach ($this->rows as $key => $row) { + $ref = & $refData[$row[$this->column]]; + $skip = & $offset[$row[$this->column]]; + if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->sqlBuilder->getOffset())) { + $ref[$key] = $row; + } else { + unset($this->rows[$key]); + } + $skip++; + unset($ref, $skip); + } + } + + $this->data = & $refData[$this->active]; + if ($this->data === NULL) { + $this->data = array(); + } else { + foreach ($this->data as $row) { + $row->setTable($this); // injects correct parent GroupedSelection + } + reset($this->data); + } + } + + + + protected function getRefTable(& $refPath) + { + $refObj = $this->refTable; + $refPath = $this->name . '.'; + while ($refObj instanceof GroupedSelection) { + $refPath .= $refObj->name . '.'; + $refObj = $refObj->refTable; + } + + return $refObj; + } + + + + /********************* manipulation ****************d*g**/ + + + + public function insert($data) + { + if ($data instanceof \Traversable && !$data instanceof Selection) { + $data = iterator_to_array($data); + } + + if (Nette\Utils\Validators::isList($data)) { + foreach (array_keys($data) as $key) { + $data[$key][$this->column] = $this->active; + } + } else { + $data[$this->column] = $this->active; + } + + return parent::insert($data); + } + + + + public function update($data) + { + $builder = $this->sqlBuilder; + + $this->sqlBuilder = new SqlBuilder($this); + $this->where($this->column, $this->active); + $return = parent::update($data); + + $this->sqlBuilder = $builder; + return $return; + } + + + + public function delete() + { + $builder = $this->sqlBuilder; + + $this->sqlBuilder = new SqlBuilder($this); + $this->where($this->column, $this->active); + $return = parent::delete(); + + $this->sqlBuilder = $builder; + return $return; + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Table/Selection.php b/apigen/libs/Nette/Nette/Database/Table/Selection.php new file mode 100644 index 00000000000..efc21d9ab96 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Table/Selection.php @@ -0,0 +1,787 @@ + ActiveRow] format */ + protected $rows; + + /** @var ActiveRow[] modifiable data in [primary key => ActiveRow] format */ + protected $data; + + /** @var Selection[] */ + protected $referenced = array(); + + /** @var array of [sqlQuery-hash => grouped data]; used by GroupedSelection */ + protected $referencing = array(); + + /** @var GroupedSelection[] cached array of GroupedSelection prototypes */ + protected $referencingPrototype = array(); + + /** @var array of [conditions => [key => ActiveRow]]; used by GroupedSelection */ + protected $aggregation = array(); + + /** @var array of touched columns */ + protected $accessed; + + /** @var array of earlier touched columns */ + protected $prevAccessed; + + /** @var bool should instance observe accessed columns caching */ + protected $observeCache = FALSE; + + /** @var bool recheck referencing keys */ + protected $checkReferenced = FALSE; + + /** @var array of primary key values */ + protected $keys = array(); + + + + /** + * Creates filtered table representation. + * @param string database table name + * @param Nette\Database\Connection + */ + public function __construct($table, Nette\Database\Connection $connection) + { + $this->name = $table; + $this->connection = $connection; + $this->primary = $connection->getDatabaseReflection()->getPrimary($table); + $this->sqlBuilder = new SqlBuilder($this); + } + + + + public function __destruct() + { + $this->saveCacheState(); + } + + + + public function __clone() + { + $this->sqlBuilder = clone $this->sqlBuilder; + $this->sqlBuilder->setSelection($this); + } + + + + /** + * @return Nette\Database\Connection + */ + public function getConnection() + { + return $this->connection; + } + + + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + + + /** + * @return string + */ + public function getPrimary() + { + return $this->primary; + } + + + + /** + * @return string + */ + public function getSql() + { + return $this->sqlBuilder->buildSelectQuery(); + } + + + + /** + * Loads cache of previous accessed columns and returns it + * @internal + * @return array|false + */ + public function getPreviousAccessed() + { + $cache = $this->connection->getCache(); + if ($this->rows === NULL && $cache && !is_string($this->prevAccessed)) { + $this->accessed = $this->prevAccessed = $cache->load(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions())); + } + + return $this->prevAccessed; + } + + + + /** + * @internal + * @return SqlBuilder + */ + public function getSqlBuilder() + { + return $this->sqlBuilder; + } + + + + /********************* quick access ****************d*g**/ + + + + /** + * Returns row specified by primary key. + * @param mixed primary key + * @return ActiveRow or FALSE if there is no such row + */ + public function get($key) + { + $clone = clone $this; + $clone->where($this->primary, $key); + return $clone->fetch(); + } + + + + /** + * Returns next row of result. + * @return ActiveRow or FALSE if there is no row + */ + public function fetch() + { + $this->execute(); + $return = current($this->data); + next($this->data); + return $return; + } + + + + /** + * Returns all rows as associative array. + * @param string + * @param string column name used for an array value or NULL for the whole row + * @return array + */ + public function fetchPairs($key, $value = NULL) + { + $return = array(); + foreach ($this as $row) { + $return[is_object($row[$key]) ? (string) $row[$key] : $row[$key]] = ($value ? $row[$value] : $row); + } + return $return; + } + + + + /********************* sql selectors ****************d*g**/ + + + + /** + * Adds select clause, more calls appends to the end. + * @param string for example "column, MD5(column) AS column_md5" + * @return Selection provides a fluent interface + */ + public function select($columns) + { + $this->emptyResultSet(); + $this->sqlBuilder->addSelect($columns); + return $this; + } + + + + /** + * Selects by primary key. + * @param mixed + * @return Selection provides a fluent interface + */ + public function find($key) + { + return $this->where($this->primary, $key); + } + + + + /** + * Adds where condition, more calls appends with AND. + * @param string condition possibly containing ? + * @param mixed + * @param mixed ... + * @return Selection provides a fluent interface + */ + public function where($condition, $parameters = array()) + { + if (is_array($condition)) { // where(array('column1' => 1, 'column2 > ?' => 2)) + foreach ($condition as $key => $val) { + if (is_int($key)) { + $this->where($val); // where('full condition') + } else { + $this->where($key, $val); // where('column', 1) + } + } + return $this; + } + + $args = func_get_args(); + if (call_user_func_array(array($this->sqlBuilder, 'addWhere'), $args)) { + $this->emptyResultSet(); + } + + return $this; + } + + + + /** + * Adds order clause, more calls appends to the end. + * @param string for example 'column1, column2 DESC' + * @return Selection provides a fluent interface + */ + public function order($columns) + { + $this->emptyResultSet(); + $this->sqlBuilder->addOrder($columns); + return $this; + } + + + + /** + * Sets limit clause, more calls rewrite old values. + * @param int + * @param int + * @return Selection provides a fluent interface + */ + public function limit($limit, $offset = NULL) + { + $this->emptyResultSet(); + $this->sqlBuilder->setLimit($limit, $offset); + return $this; + } + + + + /** + * Sets offset using page number, more calls rewrite old values. + * @param int + * @param int + * @return Selection provides a fluent interface + */ + public function page($page, $itemsPerPage) + { + return $this->limit($itemsPerPage, ($page - 1) * $itemsPerPage); + } + + + + /** + * Sets group clause, more calls rewrite old values. + * @param string + * @param string + * @return Selection provides a fluent interface + */ + public function group($columns, $having = NULL) + { + $this->emptyResultSet(); + $this->sqlBuilder->setGroup($columns, $having); + return $this; + } + + + + /********************* aggregations ****************d*g**/ + + + + /** + * Executes aggregation function. + * @param string select call in "FUNCTION(column)" format + * @return string + */ + public function aggregation($function) + { + $selection = $this->createSelectionInstance(); + $selection->getSqlBuilder()->importConditions($this->getSqlBuilder()); + $selection->select($function); + foreach ($selection->fetch() as $val) { + return $val; + } + } + + + + /** + * Counts number of rows. + * @param string if it is not provided returns count of result rows, otherwise runs new sql counting query + * @return int + */ + public function count($column = NULL) + { + if (!$column) { + $this->execute(); + return count($this->data); + } + return $this->aggregation("COUNT($column)"); + } + + + + /** + * Returns minimum value from a column. + * @param string + * @return int + */ + public function min($column) + { + return $this->aggregation("MIN($column)"); + } + + + + /** + * Returns maximum value from a column. + * @param string + * @return int + */ + public function max($column) + { + return $this->aggregation("MAX($column)"); + } + + + + /** + * Returns sum of values in a column. + * @param string + * @return int + */ + public function sum($column) + { + return $this->aggregation("SUM($column)"); + } + + + + /********************* internal ****************d*g**/ + + + + protected function execute() + { + if ($this->rows !== NULL) { + return; + } + + $this->observeCache = TRUE; + + try { + $result = $this->query($this->sqlBuilder->buildSelectQuery()); + + } catch (\PDOException $exception) { + if (!$this->sqlBuilder->getSelect() && $this->prevAccessed) { + $this->prevAccessed = ''; + $this->accessed = array(); + $result = $this->query($this->sqlBuilder->buildSelectQuery()); + } else { + throw $exception; + } + } + + $this->rows = array(); + $result->setFetchMode(PDO::FETCH_ASSOC); + foreach ($result as $key => $row) { + $row = $result->normalizeRow($row); + $this->rows[isset($row[$this->primary]) ? $row[$this->primary] : $key] = $this->createRow($row); + } + $this->data = $this->rows; + + if (isset($row[$this->primary]) && !is_string($this->accessed)) { + $this->accessed[$this->primary] = TRUE; + } + } + + + + protected function createRow(array $row) + { + return new ActiveRow($row, $this); + } + + + + protected function createSelectionInstance($table = NULL) + { + return new Selection($table ?: $this->name, $this->connection); + } + + + + protected function createGroupedSelectionInstance($table, $column) + { + return new GroupedSelection($this, $table, $column); + } + + + + protected function query($query) + { + return $this->connection->queryArgs($query, $this->sqlBuilder->getParameters()); + } + + + + protected function emptyResultSet() + { + if ($this->rows === NULL) { + return; + } + + $this->rows = NULL; + $this->saveCacheState(); + } + + + + protected function saveCacheState() + { + if ($this->observeCache && ($cache = $this->connection->getCache()) && !$this->sqlBuilder->getSelect() && $this->accessed != $this->prevAccessed) { + $cache->save(array(__CLASS__, $this->name, $this->sqlBuilder->getConditions()), $this->accessed); + } + } + + + + /** + * Returns Selection parent for caching + * @return Selection + */ + protected function getRefTable(& $refPath) + { + return $this; + } + + + + /** + * @internal + * @param string column name + * @param bool|NULL TRUE - cache, FALSE - don't cache, NULL - remove + * @return bool + */ + public function access($key, $cache = TRUE) + { + if ($cache === NULL) { + if (is_array($this->accessed)) { + $this->accessed[$key] = FALSE; + } + return FALSE; + } + + if ($key === NULL) { + $this->accessed = ''; + + } elseif (!is_string($this->accessed)) { + $this->accessed[$key] = $cache; + } + + if ($cache && !$this->sqlBuilder->getSelect() && $this->prevAccessed && ($key === NULL || !isset($this->prevAccessed[$key]))) { + $this->prevAccessed = ''; + $this->emptyResultSet(); + return TRUE; + } + + return FALSE; + } + + + + /********************* manipulation ****************d*g**/ + + + + /** + * Inserts row in a table. + * @param mixed array($column => $value)|Traversable for single row insert or Selection|string for INSERT ... SELECT + * @return ActiveRow or FALSE in case of an error or number of affected rows for INSERT ... SELECT + */ + public function insert($data) + { + if ($data instanceof Selection) { + $data = $data->getSql(); + + } elseif ($data instanceof \Traversable) { + $data = iterator_to_array($data); + } + + $return = $this->connection->query($this->sqlBuilder->buildInsertQuery(), $data); + $this->checkReferenced = TRUE; + + if (!is_array($data)) { + return $return->rowCount(); + } + + if (!isset($data[$this->primary]) && ($id = $this->connection->lastInsertId())) { + $data[$this->primary] = $id; + return $this->rows[$id] = $this->createRow($data); + + } else { + return $this->createRow($data); + + } + } + + + + /** + * Updates all rows in result set. + * Joins in UPDATE are supported only in MySQL + * @param array|\Traversable ($column => $value) + * @return int number of affected rows or FALSE in case of an error + */ + public function update($data) + { + if ($data instanceof \Traversable) { + $data = iterator_to_array($data); + + } elseif (!is_array($data)) { + throw new Nette\InvalidArgumentException; + } + + if (!$data) { + return 0; + } + + return $this->connection->queryArgs( + $this->sqlBuilder->buildUpdateQuery(), + array_merge(array($data), $this->sqlBuilder->getParameters()) + )->rowCount(); + } + + + + /** + * Deletes all rows in result set. + * @return int number of affected rows or FALSE in case of an error + */ + public function delete() + { + return $this->query($this->sqlBuilder->buildDeleteQuery())->rowCount(); + } + + + + /********************* references ****************d*g**/ + + + + /** + * Returns referenced row. + * @param string + * @param string + * @param bool checks if rows contains the same primary value relations + * @return Selection or array() if the row does not exist + */ + public function getReferencedTable($table, $column, $checkReferenced = FALSE) + { + $referenced = & $this->getRefTable($refPath)->referenced[$refPath . "$table.$column"]; + if ($referenced === NULL || $checkReferenced || $this->checkReferenced) { + $this->execute(); + $this->checkReferenced = FALSE; + $keys = array(); + foreach ($this->rows as $row) { + if ($row[$column] === NULL) + continue; + + $key = $row[$column] instanceof ActiveRow ? $row[$column]->getPrimary() : $row[$column]; + $keys[$key] = TRUE; + } + + if ($referenced !== NULL && array_keys($keys) === array_keys($referenced->rows)) { + return $referenced; + } + + if ($keys) { + $referenced = $this->createSelectionInstance($table); + $referenced->where($referenced->primary, array_keys($keys)); + } else { + $referenced = array(); + } + } + + return $referenced; + } + + + + /** + * Returns referencing rows. + * @param string + * @param string + * @param int primary key + * @return GroupedSelection + */ + public function getReferencingTable($table, $column, $active = NULL) + { + $prototype = & $this->getRefTable($refPath)->referencingPrototype[$refPath . "$table.$column"]; + if (!$prototype) { + $prototype = $this->createGroupedSelectionInstance($table, $column); + $prototype->where("$table.$column", array_keys((array) $this->rows)); + } + + $clone = clone $prototype; + $clone->setActive($active); + return $clone; + } + + + + /********************* interface Iterator ****************d*g**/ + + + + public function rewind() + { + $this->execute(); + $this->keys = array_keys($this->data); + reset($this->keys); + } + + + + /** @return ActiveRow */ + public function current() + { + return $this->data[current($this->keys)]; + } + + + + /** + * @return string row ID + */ + public function key() + { + return current($this->keys); + } + + + + public function next() + { + next($this->keys); + } + + + + public function valid() + { + return current($this->keys) !== FALSE; + } + + + + /********************* interface ArrayAccess ****************d*g**/ + + + + /** + * Mimic row. + * @param string row ID + * @param ActiveRow + * @return NULL + */ + public function offsetSet($key, $value) + { + $this->execute(); + $this->data[$key] = $value; + } + + + + /** + * Returns specified row. + * @param string row ID + * @return ActiveRow or NULL if there is no such row + */ + public function offsetGet($key) + { + $this->execute(); + return $this->data[$key]; + } + + + + /** + * Tests if row exists. + * @param string row ID + * @return bool + */ + public function offsetExists($key) + { + $this->execute(); + return isset($this->data[$key]); + } + + + + /** + * Removes row from result set. + * @param string row ID + * @return NULL + */ + public function offsetUnset($key) + { + $this->execute(); + unset($this->data[$key]); + } + +} diff --git a/apigen/libs/Nette/Nette/Database/Table/SqlBuilder.php b/apigen/libs/Nette/Nette/Database/Table/SqlBuilder.php new file mode 100644 index 00000000000..c652420f966 --- /dev/null +++ b/apigen/libs/Nette/Nette/Database/Table/SqlBuilder.php @@ -0,0 +1,383 @@ +selection = $selection; + $this->connection = $selection->getConnection(); + $this->delimitedTable = $this->connection->getSupplementalDriver()->delimite($selection->getName()); + } + + + + public function setSelection(Selection $selection) + { + $this->selection = $selection; + } + + + + public function buildInsertQuery() + { + return "INSERT INTO {$this->delimitedTable}"; + } + + + + public function buildUpdateQuery() + { + return "UPDATE{$this->buildTopClause()} {$this->delimitedTable} SET ?" . $this->buildConditions(); + } + + + + public function buildDeleteQuery() + { + return "DELETE{$this->buildTopClause()} FROM {$this->delimitedTable}" . $this->buildConditions(); + } + + + + public function importConditions(SqlBuilder $builder) + { + $this->where = $builder->where; + $this->parameters = $builder->parameters; + $this->conditions = $builder->conditions; + } + + + + /********************* SQL selectors ****************d*g**/ + + + + public function addSelect($columns) + { + $this->select[] = $columns; + } + + + + public function getSelect() + { + return $this->select; + } + + + + public function addWhere($condition, $parameters = array()) + { + $args = func_get_args(); + $hash = md5(json_encode($args)); + if (isset($this->conditions[$hash])) { + return FALSE; + } + + $this->conditions[$hash] = $condition; + $condition = $this->removeExtraTables($condition); + $condition = $this->tryDelimite($condition); + + if (count($args) !== 2 || strpbrk($condition, '?:')) { // where('column < ? OR column > ?', array(1, 2)) + if (count($args) !== 2 || !is_array($parameters)) { // where('column < ? OR column > ?', 1, 2) + $parameters = $args; + array_shift($parameters); + } + + $this->parameters = array_merge($this->parameters, $parameters); + + } elseif ($parameters === NULL) { // where('column', NULL) + $condition .= ' IS NULL'; + + } elseif ($parameters instanceof Selection) { // where('column', $db->$table()) + $clone = clone $parameters; + if (!$clone->getSqlBuilder()->select) { + $clone->select($clone->primary); + } + + if ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) !== 'mysql') { + $condition .= ' IN (' . $clone->getSql() . ')'; + } else { + $in = array(); + foreach ($clone as $row) { + $this->parameters[] = array_values(iterator_to_array($row)); + $in[] = (count($row) === 1 ? '?' : '(?)'); + } + $condition .= ' IN (' . ($in ? implode(', ', $in) : 'NULL') . ')'; + } + + } elseif (!is_array($parameters)) { // where('column', 'x') + $condition .= ' = ?'; + $this->parameters[] = $parameters; + + } else { // where('column', array(1, 2)) + if ($parameters) { + $condition .= " IN (?)"; + $this->parameters[] = $parameters; + } else { + $condition .= " IN (NULL)"; + } + } + + $this->where[] = $condition; + return TRUE; + } + + + + public function getConditions() + { + return array_values($this->conditions); + } + + + + public function addOrder($columns) + { + $this->order[] = $columns; + } + + + + public function getOrder() + { + return $this->order; + } + + + + public function setLimit($limit, $offset) + { + $this->limit = $limit; + $this->offset = $offset; + } + + + + public function getLimit() + { + return $this->limit; + } + + + + public function getOffset() + { + return $this->offset; + } + + + + public function setGroup($columns, $having) + { + $this->group = $columns; + $this->having = $having; + } + + + + public function getGroup() + { + return $this->group; + } + + + + public function getHaving() + { + return $this->having; + } + + + + /********************* SQL building ****************d*g**/ + + + + /** + * Returns SQL query. + * @return string + */ + public function buildSelectQuery() + { + $join = $this->buildJoins(implode(',', $this->conditions), TRUE); + $join += $this->buildJoins(implode(',', $this->select) . ",{$this->group},{$this->having}," . implode(',', $this->order)); + + $prefix = $join ? "{$this->delimitedTable}." : ''; + if ($this->select) { + $cols = $this->tryDelimite($this->removeExtraTables(implode(', ', $this->select))); + + } elseif ($prevAccessed = $this->selection->getPreviousAccessed()) { + $cols = array_map(array($this->connection->getSupplementalDriver(), 'delimite'), array_keys(array_filter($prevAccessed))); + $cols = $prefix . implode(', ' . $prefix, $cols); + + } else { + $cols = $prefix . '*'; + } + + return "SELECT{$this->buildTopClause()} {$cols} FROM {$this->delimitedTable}" . implode($join) . $this->buildConditions(); + } + + + + public function getParameters() + { + return $this->parameters; + } + + + + protected function buildJoins($val, $inner = FALSE) + { + $driver = $this->selection->getConnection()->getSupplementalDriver(); + $reflection = $this->selection->getConnection()->getDatabaseReflection(); + $joins = array(); + preg_match_all('~\\b([a-z][\\w.:]*[.:])([a-z]\\w*|\*)(\\s+IS\\b|\\s*<=>)?~i', $val, $matches); + foreach ($matches[1] as $names) { + $parent = $this->selection->getName(); + if ($names !== "$parent.") { // case-sensitive + preg_match_all('~\\b([a-z][\\w]*|\*)([.:])~i', $names, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + list(, $name, $delimiter) = $match; + + if ($delimiter === ':') { + list($table, $primary) = $reflection->getHasManyReference($parent, $name); + $column = $reflection->getPrimary($parent); + } else { + list($table, $column) = $reflection->getBelongsToReference($parent, $name); + $primary = $reflection->getPrimary($table); + } + + $joins[$name] = ' ' + . (!isset($joins[$name]) && $inner && !isset($match[3]) ? 'INNER' : 'LEFT') + . ' JOIN ' . $driver->delimite($table) . ($table !== $name ? ' AS ' . $driver->delimite($name) : '') + . ' ON ' . $driver->delimite($parent) . '.' . $driver->delimite($column) + . ' = ' . $driver->delimite($name) . '.' . $driver->delimite($primary); + + $parent = $name; + } + } + } + return $joins; + } + + + + protected function buildConditions() + { + $return = ''; + $driver = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME); + $where = $this->where; + if ($this->limit !== NULL && $driver === 'oci') { + $where[] = ($this->offset ? "rownum > $this->offset AND " : '') . 'rownum <= ' . ($this->limit + $this->offset); + } + if ($where) { + $return .= ' WHERE (' . implode(') AND (', $where) . ')'; + } + if ($this->group) { + $return .= ' GROUP BY '. $this->tryDelimite($this->removeExtraTables($this->group)); + } + if ($this->having) { + $return .= ' HAVING '. $this->tryDelimite($this->removeExtraTables($this->having)); + } + if ($this->order) { + $return .= ' ORDER BY ' . $this->tryDelimite($this->removeExtraTables(implode(', ', $this->order))); + } + if ($this->limit !== NULL && $driver !== 'oci' && $driver !== 'dblib') { + $return .= " LIMIT $this->limit"; + if ($this->offset !== NULL) { + $return .= " OFFSET $this->offset"; + } + } + return $return; + } + + + + protected function buildTopClause() + { + if ($this->limit !== NULL && $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) === 'dblib') { + return " TOP ($this->limit)"; //! offset is not supported + } + return ''; + } + + + + protected function tryDelimite($s) + { + $driver = $this->connection->getSupplementalDriver(); + return preg_replace_callback('#(?<=[^\w`"\[]|^)[a-z_][a-z0-9_]*(?=[^\w`"(\]]|$)#i', function($m) use ($driver) { + return strtoupper($m[0]) === $m[0] ? $m[0] : $driver->delimite($m[0]); + }, $s); + } + + + + protected function removeExtraTables($expression) + { + return preg_replace('~(?:\\b[a-z_][a-z0-9_.:]*[.:])?([a-z_][a-z0-9_]*)[.:]([a-z_*])~i', '\\1.\\2', $expression); // rewrite tab1.tab2.col + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/Bar.php b/apigen/libs/Nette/Nette/Diagnostics/Bar.php new file mode 100644 index 00000000000..cfe2d680fbe --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/Bar.php @@ -0,0 +1,79 @@ +panels[$id])); + } + $this->panels[$id] = $panel; + return $this; + } + + + + /** + * Renders debug bar. + * @return void + */ + public function render() + { + $obLevel = ob_get_level(); + $panels = array(); + foreach ($this->panels as $id => $panel) { + try { + $panels[] = array( + 'id' => preg_replace('#[^a-z0-9]+#i', '-', $id), + 'tab' => $tab = (string) $panel->getTab(), + 'panel' => $tab ? (string) $panel->getPanel() : NULL, + ); + } catch (\Exception $e) { + $panels[] = array( + 'id' => "error-" . preg_replace('#[^a-z0-9]+#i', '-', $id), + 'tab' => "Error in $id", + 'panel' => '

Error: ' . $id . '

' . nl2br(htmlSpecialChars($e)) . '
', + ); + while (ob_get_level() > $obLevel) { // restore ob-level if broken + ob_end_clean(); + } + } + } + require __DIR__ . '/templates/bar.phtml'; + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/BlueScreen.php b/apigen/libs/Nette/Nette/Diagnostics/BlueScreen.php new file mode 100644 index 00000000000..f9ce51ecbf9 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/BlueScreen.php @@ -0,0 +1,139 @@ +panels, TRUE)) { + $this->panels[] = $panel; + } + return $this; + } + + + + /** + * Renders blue screen. + * @param \Exception + * @return void + */ + public function render(\Exception $exception) + { + $panels = $this->panels; + require __DIR__ . '/templates/bluescreen.phtml'; + } + + + + /** + * Returns syntax highlighted source code. + * @param string + * @param int + * @param int + * @return string + */ + public static function highlightFile($file, $line, $lines = 15, $vars = array()) + { + $source = @file_get_contents($file); // intentionally @ + if ($source) { + return static::highlightPhp($source, $line, $lines, $vars); + } + } + + + + /** + * Returns syntax highlighted source code. + * @param string + * @param int + * @param int + * @return string + */ + public static function highlightPhp($source, $line, $lines = 15, $vars = array()) + { + if (function_exists('ini_set')) { + ini_set('highlight.comment', '#998; font-style: italic'); + ini_set('highlight.default', '#000'); + ini_set('highlight.html', '#06B'); + ini_set('highlight.keyword', '#D24; font-weight: bold'); + ini_set('highlight.string', '#080'); + } + + $source = str_replace(array("\r\n", "\r"), "\n", $source); + $source = explode("\n", highlight_string($source, TRUE)); + $spans = 1; + $out = $source[0]; // + $source = explode('
', $source[1]); + array_unshift($source, NULL); + + $start = $i = max(1, $line - floor($lines * 2/3)); + while (--$i >= 1) { // find last highlighted block + if (preg_match('#.*(]*>)#', $source[$i], $m)) { + if ($m[1] !== '
') { + $spans++; $out .= $m[1]; + } + break; + } + } + + $source = array_slice($source, $start, $lines, TRUE); + end($source); + $numWidth = strlen((string) key($source)); + + foreach ($source as $n => $s) { + $spans += substr_count($s, ']+>#', $s, $tags); + if ($n == $line) { + $out .= sprintf( + "%{$numWidth}s: %s\n%s", + $n, + strip_tags($s), + implode('', $tags[0]) + ); + } else { + $out .= sprintf("%{$numWidth}s: %s\n", $n, $s); + } + } + $out .= str_repeat('
', $spans) . ''; + + $out = preg_replace_callback('#">\$(\w+)( )?#', function($m) use ($vars) { + return isset($vars[$m[1]]) + ? '" title="' . str_replace('"', '"', strip_tags(Helpers::htmlDump($vars[$m[1]]))) . $m[0] + : $m[0]; + }, $out); + + return "
$out
"; + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/Debugger.php b/apigen/libs/Nette/Nette/Diagnostics/Debugger.php new file mode 100644 index 00000000000..f564f263b49 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/Debugger.php @@ -0,0 +1,694 @@ + '1;33', + 'null' => '1;33', + 'int' => '1;36', + 'float' => '1;36', + 'string' => '1;32', + 'array' => '1;31', + 'key' => '1;37', + 'object' => '1;31', + 'visibility' => '1;30', + 'resource' => '1;37', + ); + + /********************* errors and exceptions reporting ****************d*g**/ + + /** server modes {@link Debugger::enable()} */ + const DEVELOPMENT = FALSE, + PRODUCTION = TRUE, + DETECT = NULL; + + /** @var BlueScreen */ + public static $blueScreen; + + /** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */ + public static $strictMode = FALSE; // $immediateDeath + + /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */ + public static $scream = FALSE; + + /** @var array of callables specifies the functions that are automatically called after fatal error */ + public static $onFatalError = array(); + + /** @var bool {@link Debugger::enable()} */ + private static $enabled = FALSE; + + /** @var mixed {@link Debugger::tryError()} FALSE means catching is disabled */ + private static $lastError = FALSE; + + /********************* logging ****************d*g**/ + + /** @var Logger */ + public static $logger; + + /** @var FireLogger */ + public static $fireLogger; + + /** @var string name of the directory where errors should be logged; FALSE means that logging is disabled */ + public static $logDirectory; + + /** @var string email to sent error notifications */ + public static $email; + + /** @deprecated */ + public static $mailer; + + /** @deprecated */ + public static $emailSnooze; + + /********************* debug bar ****************d*g**/ + + /** @var Bar */ + public static $bar; + + /** @var DefaultBarPanel */ + private static $errorPanel; + + /** @var DefaultBarPanel */ + private static $dumpPanel; + + /********************* Firebug extension ****************d*g**/ + + /** {@link Debugger::log()} and {@link Debugger::fireLog()} */ + const DEBUG = 'debug', + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', + CRITICAL = 'critical'; + + + + /** + * Static class - cannot be instantiated. + */ + final public function __construct() + { + throw new Nette\StaticClassException; + } + + + + /** + * Static class constructor. + * @internal + */ + public static function _init() + { + self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE); + self::$consoleMode = PHP_SAPI === 'cli'; + self::$productionMode = self::DETECT; + if (self::$consoleMode) { + self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . implode(' ', $_SERVER['argv']); + } else { + self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; + if (isset($_SERVER['REQUEST_URI'])) { + self::$source = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') + . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')) + . $_SERVER['REQUEST_URI']; + } + } + + self::$logger = new Logger; + self::$logDirectory = & self::$logger->directory; + self::$email = & self::$logger->email; + self::$mailer = & self::$logger->mailer; + self::$emailSnooze = & Logger::$emailSnooze; + + self::$fireLogger = new FireLogger; + + self::$blueScreen = new BlueScreen; + self::$blueScreen->addPanel(function($e) { + if ($e instanceof Nette\Templating\FilterException) { + return array( + 'tab' => 'Template', + 'panel' => '

File: ' . Helpers::editorLink($e->sourceFile, $e->sourceLine) + . '  Line: ' . ($e->sourceLine ? $e->sourceLine : 'n/a') . '

' + . ($e->sourceLine ? BlueScreen::highlightFile($e->sourceFile, $e->sourceLine) : '') + ); + } elseif ($e instanceof Nette\Utils\NeonException && preg_match('#line (\d+)#', $e->getMessage(), $m)) { + if ($item = Helpers::findTrace($e->getTrace(), 'Nette\Config\Adapters\NeonAdapter::load')) { + return array( + 'tab' => 'NEON', + 'panel' => '

File: ' . Helpers::editorLink($item['args'][0], $m[1]) . '  Line: ' . $m[1] . '

' + . BlueScreen::highlightFile($item['args'][0], $m[1]) + ); + } elseif ($item = Helpers::findTrace($e->getTrace(), 'Nette\Utils\Neon::decode')) { + return array( + 'tab' => 'NEON', + 'panel' => BlueScreen::highlightPhp($item['args'][0], $m[1]) + ); + } + } + }); + + self::$bar = new Bar; + self::$bar->addPanel(new DefaultBarPanel('time')); + self::$bar->addPanel(new DefaultBarPanel('memory')); + self::$bar->addPanel(self::$errorPanel = new DefaultBarPanel('errors')); // filled by _errorHandler() + self::$bar->addPanel(self::$dumpPanel = new DefaultBarPanel('dumps')); // filled by barDump() + } + + + + /********************* errors and exceptions reporting ****************d*g**/ + + + + /** + * Enables displaying or logging errors and exceptions. + * @param mixed production, development mode, autodetection or IP address(es) whitelist. + * @param string error log directory; enables logging in production mode, FALSE means that logging is disabled + * @param string administrator email; enables email sending in production mode + * @return void + */ + public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL) + { + error_reporting(E_ALL | E_STRICT); + + // production/development mode detection + if (is_bool($mode)) { + self::$productionMode = $mode; + + } elseif ($mode !== self::DETECT || self::$productionMode === NULL) { // IP addresses or computer names whitelist detection + $list = is_string($mode) ? preg_split('#[,\s]+#', $mode) : (array) $mode; + if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $list[] = '127.0.0.1'; + $list[] = '::1'; + } + self::$productionMode = !in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE); + } + + // logging configuration + if (is_string($logDirectory)) { + self::$logDirectory = realpath($logDirectory); + if (self::$logDirectory === FALSE) { + throw new Nette\DirectoryNotFoundException("Directory '$logDirectory' is not found."); + } + } elseif ($logDirectory === FALSE) { + self::$logDirectory = FALSE; + + } elseif (self::$logDirectory === NULL) { + self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log'; + } + if (self::$logDirectory) { + ini_set('error_log', self::$logDirectory . '/php_error.log'); + } + + // php configuration + if (function_exists('ini_set')) { + ini_set('display_errors', !self::$productionMode); // or 'stderr' + ini_set('html_errors', FALSE); + ini_set('log_errors', FALSE); + + } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally == + throw new Nette\NotSupportedException('Function ini_set() must be enabled.'); + } + + if ($email) { + if (!is_string($email)) { + throw new Nette\InvalidArgumentException('Email address must be a string.'); + } + self::$email = $email; + } + + if (!defined('E_DEPRECATED')) { + define('E_DEPRECATED', 8192); + } + + if (!defined('E_USER_DEPRECATED')) { + define('E_USER_DEPRECATED', 16384); + } + + if (!self::$enabled) { + register_shutdown_function(array(__CLASS__, '_shutdownHandler')); + set_exception_handler(array(__CLASS__, '_exceptionHandler')); + set_error_handler(array(__CLASS__, '_errorHandler')); + self::$enabled = TRUE; + } + } + + + + /** + * Is Debug enabled? + * @return bool + */ + public static function isEnabled() + { + return self::$enabled; + } + + + + /** + * Logs message or exception to file (if not disabled) and sends email notification (if enabled). + * @param string|Exception + * @param int one of constant Debugger::INFO, WARNING, ERROR (sends email), CRITICAL (sends email) + * @return string logged error filename + */ + public static function log($message, $priority = self::INFO) + { + if (self::$logDirectory === FALSE) { + return; + + } elseif (!self::$logDirectory) { + throw new Nette\InvalidStateException('Logging directory is not specified in Nette\Diagnostics\Debugger::$logDirectory.'); + } + + if ($message instanceof \Exception) { + $exception = $message; + $message = ($message instanceof Nette\FatalErrorException + ? 'Fatal error: ' . $exception->getMessage() + : get_class($exception) . ": " . $exception->getMessage()) + . " in " . $exception->getFile() . ":" . $exception->getLine(); + + $hash = md5($exception /*5.2*. (method_exists($exception, 'getPrevious') ? $exception->getPrevious() : (isset($exception->previous) ? $exception->previous : ''))*/); + $exceptionFilename = "exception-" . @date('Y-m-d-H-i-s') . "-$hash.html"; + foreach (new \DirectoryIterator(self::$logDirectory) as $entry) { + if (strpos($entry, $hash)) { + $exceptionFilename = $entry; + $saved = TRUE; + break; + } + } + } + + self::$logger->log(array( + @date('[Y-m-d H-i-s]'), + trim($message), + self::$source ? ' @ ' . self::$source : NULL, + !empty($exceptionFilename) ? ' @@ ' . $exceptionFilename : NULL + ), $priority); + + if (!empty($exceptionFilename)) { + $exceptionFilename = self::$logDirectory . '/' . $exceptionFilename; + if (empty($saved) && $logHandle = @fopen($exceptionFilename, 'w')) { + ob_start(); // double buffer prevents sending HTTP headers in some PHP + ob_start(function($buffer) use ($logHandle) { fwrite($logHandle, $buffer); }, 4096); + self::$blueScreen->render($exception); + ob_end_flush(); + ob_end_clean(); + fclose($logHandle); + } + return strtr($exceptionFilename, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR); + } + } + + + + /** + * Shutdown handler to catch fatal errors and execute of the planned activities. + * @return void + * @internal + */ + public static function _shutdownHandler() + { + if (!self::$enabled) { + return; + } + + // fatal error handler + static $types = array( + E_ERROR => 1, + E_CORE_ERROR => 1, + E_COMPILE_ERROR => 1, + E_PARSE => 1, + ); + $error = error_get_last(); + if (isset($types[$error['type']])) { + self::_exceptionHandler(new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL)); + } + + // debug bar (require HTML & development mode) + if (self::$bar && !self::$productionMode && self::isHtmlMode()) { + self::$bar->render(); + } + } + + + + /** + * Handler to catch uncaught exception. + * @param \Exception + * @return void + * @internal + */ + public static function _exceptionHandler(\Exception $exception) + { + if (!headers_sent()) { // for PHP < 5.2.4 + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + header($protocol . ' 500', TRUE, 500); + } + + try { + if (self::$productionMode) { + try { + self::log($exception, self::ERROR); + } catch (\Exception $e) { + echo 'FATAL ERROR: unable to log error'; + } + + if (self::$consoleMode) { + echo "ERROR: the server encountered an internal error and was unable to complete your request.\n"; + + } elseif (self::isHtmlMode()) { + require __DIR__ . '/templates/error.phtml'; + } + + } else { + if (self::$consoleMode) { // dump to console + echo "$exception\n"; + if ($file = self::log($exception)) { + echo "(stored in $file)\n"; + if (self::$browser) { + exec(self::$browser . ' ' . escapeshellarg($file)); + } + } + + } elseif (self::isHtmlMode()) { // dump to browser + self::$blueScreen->render($exception); + if (self::$bar) { + self::$bar->render(); + } + + } elseif (!self::fireLog($exception, self::ERROR)) { // AJAX or non-HTML mode + $file = self::log($exception); + if (!headers_sent()) { + header("X-Nette-Error-Log: $file"); + } + } + } + + foreach (self::$onFatalError as $handler) { + call_user_func($handler, $exception); + } + + } catch (\Exception $e) { + if (self::$productionMode) { + echo self::isHtmlMode() ? 'FATAL ERROR' : 'FATAL ERROR'; + } else { + echo "FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(), + "\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n"; + } + } + + self::$enabled = FALSE; // un-register shutdown function + exit(255); + } + + + + /** + * Handler to catch warnings and notices. + * @param int level of the error raised + * @param string error message + * @param string file that the error was raised in + * @param int line number the error was raised at + * @param array an array of variables that existed in the scope the error was triggered in + * @return bool FALSE to call normal error handler, NULL otherwise + * @throws Nette\FatalErrorException + * @internal + */ + public static function _errorHandler($severity, $message, $file, $line, $context) + { + if (self::$scream) { + error_reporting(E_ALL | E_STRICT); + } + + if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) { // tryError mode + self::$lastError = new \ErrorException($message, 0, $severity, $file, $line); + return NULL; + } + + if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) { + throw new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context); + + } elseif (($severity & error_reporting()) !== $severity) { + return FALSE; // calls normal error handler to fill-in error_get_last() + + } elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) { + self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context)); + } + + static $types = array( + E_WARNING => 'Warning', + E_COMPILE_WARNING => 'Warning', // currently unable to handle + E_USER_WARNING => 'Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'Notice', + E_STRICT => 'Strict standards', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'Deprecated', + ); + + $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message"; + $count = & self::$errorPanel->data["$message|$file|$line"]; + + if ($count++) { // repeated error + return NULL; + + } elseif (self::$productionMode) { + self::log("$message in $file:$line", self::ERROR); + return NULL; + + } else { + $ok = self::fireLog(new \ErrorException($message, 0, $severity, $file, $line), self::WARNING); + return !self::isHtmlMode() || (!self::$bar && !$ok) ? FALSE : NULL; + } + + return FALSE; // call normal error handler + } + + + + /** + * Handles exception thrown in __toString(). + * @param \Exception + * @return void + */ + public static function toStringException(\Exception $exception) + { + if (self::$enabled) { + self::_exceptionHandler($exception); + } else { + trigger_error($exception->getMessage(), E_USER_ERROR); + } + } + + + + /** + * Starts catching potential errors/warnings. + * @return void + */ + public static function tryError() + { + if (!self::$enabled && self::$lastError === FALSE) { + set_error_handler(array(__CLASS__, '_errorHandler')); + } + self::$lastError = NULL; + } + + + + /** + * Returns catched error/warning message. + * @param \ErrorException catched error + * @return bool + */ + public static function catchError(& $error) + { + if (!self::$enabled && self::$lastError !== FALSE) { + restore_error_handler(); + } + $error = self::$lastError; + self::$lastError = FALSE; + return (bool) $error; + } + + + + /********************* useful tools ****************d*g**/ + + + + /** + * Dumps information about a variable in readable format. + * @param mixed variable to dump + * @param bool return output instead of printing it? (bypasses $productionMode) + * @return mixed variable itself or dump + */ + public static function dump($var, $return = FALSE) + { + if (!$return && self::$productionMode) { + return $var; + } + + $output = "
" . Helpers::htmlDump($var) . "
\n"; + + if (!$return) { + $trace = /*5.2*PHP_VERSION_ID < 50205 ? debug_backtrace() : */debug_backtrace(FALSE); + $i = Helpers::findTrace($trace, 'dump') ? 1 : 0; + if (isset($trace[$i]['file'], $trace[$i]['line']) && is_file($trace[$i]['file'])) { + $lines = file($trace[$i]['file']); + preg_match('#dump\((.*)\)#', $lines[$trace[$i]['line'] - 1], $m); + $output = substr_replace( + $output, + ' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$trace[$i]['file']} on line {$trace[$i]['line']}") . '"', + 4, 0); + + if (self::$showLocation) { + $output = substr_replace( + $output, + ' in ' . Helpers::editorLink($trace[$i]['file'], $trace[$i]['line']) . ":{$trace[$i]['line']}", + -8, 0); + } + } + } + + if (self::$consoleMode) { + if (self::$consoleColors && substr(getenv('TERM'), 0, 5) === 'xterm') { + $output = preg_replace_callback('#|#', function($m) { + return "\033[" . (isset($m[1], Debugger::$consoleColors[$m[1]]) ? Debugger::$consoleColors[$m[1]] : '0') . "m"; + }, $output); + } + $output = htmlspecialchars_decode(strip_tags($output), ENT_QUOTES); + } + + if ($return) { + return $output; + + } else { + echo $output; + return $var; + } + } + + + + /** + * Starts/stops stopwatch. + * @param string name + * @return float elapsed seconds + */ + public static function timer($name = NULL) + { + static $time = array(); + $now = microtime(TRUE); + $delta = isset($time[$name]) ? $now - $time[$name] : 0; + $time[$name] = $now; + return $delta; + } + + + + /** + * Dumps information about a variable in Nette Debug Bar. + * @param mixed variable to dump + * @param string optional title + * @return mixed variable itself + */ + public static function barDump($var, $title = NULL) + { + if (!self::$productionMode) { + $dump = array(); + foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) { + $dump[$key] = Helpers::clickableDump($val); + } + self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump); + } + return $var; + } + + + + /** + * Sends message to FireLogger console. + * @param mixed message to log + * @return bool was successful? + */ + public static function fireLog($message) + { + if (!self::$productionMode) { + return self::$fireLogger->log($message); + } + } + + + + private static function isHtmlMode() + { + return !self::$ajaxDetected && !self::$consoleMode + && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list())); + } + + + + /** @deprecated */ + public static function addPanel(IBarPanel $panel, $id = NULL) + { + return self::$bar->addPanel($panel, $id); + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/DefaultBarPanel.php b/apigen/libs/Nette/Nette/Diagnostics/DefaultBarPanel.php new file mode 100644 index 00000000000..c969a79c7a5 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/DefaultBarPanel.php @@ -0,0 +1,76 @@ +id = $id; + } + + + + /** + * Renders HTML code for custom tab. + * @return string + */ + public function getTab() + { + ob_start(); + $data = $this->data; + if ($this->id === 'time') { + require __DIR__ . '/templates/bar.time.tab.phtml'; + } elseif ($this->id === 'memory') { + require __DIR__ . '/templates/bar.memory.tab.phtml'; + } elseif ($this->id === 'dumps' && $this->data) { + require __DIR__ . '/templates/bar.dumps.tab.phtml'; + } elseif ($this->id === 'errors' && $this->data) { + require __DIR__ . '/templates/bar.errors.tab.phtml'; + } + return ob_get_clean(); + } + + + + /** + * Renders HTML code for custom panel. + * @return string + */ + public function getPanel() + { + ob_start(); + $data = $this->data; + if ($this->id === 'dumps') { + require __DIR__ . '/templates/bar.dumps.panel.phtml'; + } elseif ($this->id === 'errors') { + require __DIR__ . '/templates/bar.errors.panel.phtml'; + } + return ob_get_clean(); + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/FireLogger.php b/apigen/libs/Nette/Nette/Diagnostics/FireLogger.php new file mode 100644 index 00000000000..a8e9b3de534 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/FireLogger.php @@ -0,0 +1,188 @@ + array()); + + + + /** + * Sends message to FireLogger console. + * @param mixed + * @return bool was successful? + */ + public static function log($message, $priority = self::DEBUG) + { + if (!isset($_SERVER['HTTP_X_FIRELOGGER']) || headers_sent()) { + return FALSE; + } + + $item = array( + 'name' => 'PHP', + 'level' => $priority, + 'order' => count(self::$payload['logs']), + 'time' => str_pad(number_format((microtime(TRUE) - Debugger::$time) * 1000, 1, '.', ' '), 8, '0', STR_PAD_LEFT) . ' ms', + 'template' => '', + 'message' => '', + 'style' => 'background:#767ab6', + ); + + $args = func_get_args(); + if (isset($args[0]) && is_string($args[0])) { + $item['template'] = array_shift($args); + } + + if (isset($args[0]) && $args[0] instanceof \Exception) { + $e = array_shift($args); + $trace = $e->getTrace(); + if (isset($trace[0]['class']) && $trace[0]['class'] === 'Nette\Diagnostics\Debugger' + && ($trace[0]['function'] === '_shutdownHandler' || $trace[0]['function'] === '_errorHandler') + ) { + unset($trace[0]); + } + + $file = str_replace(dirname(dirname(dirname($e->getFile()))), "\xE2\x80\xA6", $e->getFile()); + $item['template'] = ($e instanceof \ErrorException ? '' : get_class($e) . ': ') + . $e->getMessage() . ($e->getCode() ? ' #' . $e->getCode() : '') . ' in ' . $file . ':' . $e->getLine(); + $item['pathname'] = $e->getFile(); + $item['lineno'] = $e->getLine(); + + } else { + $trace = debug_backtrace(); + if (isset($trace[1]['class']) && $trace[1]['class'] === 'Nette\Diagnostics\Debugger' + && ($trace[1]['function'] === 'fireLog') + ) { + unset($trace[0]); + } + + foreach ($trace as $frame) { + if (isset($frame['file']) && is_file($frame['file'])) { + $item['pathname'] = $frame['file']; + $item['lineno'] = $frame['line']; + break; + } + } + } + + $item['exc_info'] = array('', '', array()); + $item['exc_frames'] = array(); + + foreach ($trace as $frame) { + $frame += array('file' => NULL, 'line' => NULL, 'class' => NULL, 'type' => NULL, 'function' => NULL, 'object' => NULL, 'args' => NULL); + $item['exc_info'][2][] = array($frame['file'], $frame['line'], "$frame[class]$frame[type]$frame[function]", $frame['object']); + $item['exc_frames'][] = $frame['args']; + } + + if (isset($args[0]) && in_array($args[0], array(self::DEBUG, self::INFO, self::WARNING, self::ERROR, self::CRITICAL), TRUE)) { + $item['level'] = array_shift($args); + } + + $item['args'] = $args; + + self::$payload['logs'][] = self::jsonDump($item, -1); + foreach (str_split(base64_encode(@json_encode(self::$payload)), 4990) as $k => $v) { // intentionally @ + header("FireLogger-de11e-$k:$v"); + } + return TRUE; + } + + + + /** + * Dump implementation for JSON. + * @param mixed variable to dump + * @param int current recursion level + * @return string + */ + private static function jsonDump(&$var, $level = 0) + { + if (is_bool($var) || is_null($var) || is_int($var) || is_float($var)) { + return $var; + + } elseif (is_string($var)) { + if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { + $var = substr($var, 0, Debugger::$maxLen) . " \xE2\x80\xA6 "; + } + return Nette\Utils\Strings::fixEncoding($var); + + } elseif (is_array($var)) { + static $marker; + if ($marker === NULL) { + $marker = uniqid("\x00", TRUE); + } + if (isset($var[$marker])) { + return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $var[$marker] = TRUE; + $res = array(); + foreach ($var as $k => &$v) { + if ($k !== $marker) { + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); + } + } + unset($var[$marker]); + return $res; + + } else { + return " \xE2\x80\xA6 "; + } + + } elseif (is_object($var)) { + $arr = (array) $var; + static $list = array(); + if (in_array($var, $list, TRUE)) { + return "\xE2\x80\xA6RECURSION\xE2\x80\xA6"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $list[] = $var; + $res = array("\x00" => '(object) ' . get_class($var)); + foreach ($arr as $k => &$v) { + if ($k[0] === "\x00") { + $k = substr($k, strrpos($k, "\x00") + 1); + } + $res[self::jsonDump($k)] = self::jsonDump($v, $level + 1); + } + array_pop($list); + return $res; + + } else { + return " \xE2\x80\xA6 "; + } + + } elseif (is_resource($var)) { + return "resource " . get_resource_type($var); + + } else { + return "unknown type"; + } + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/Helpers.php b/apigen/libs/Nette/Nette/Diagnostics/Helpers.php new file mode 100644 index 00000000000..6361256b885 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/Helpers.php @@ -0,0 +1,233 @@ +href(strtr(Debugger::$editor, array('%file' => rawurlencode($file), '%line' => $line))) + ->title("$file:$line") + ->setHtml(htmlSpecialChars(rtrim($dir, DIRECTORY_SEPARATOR)) . DIRECTORY_SEPARATOR . '' . htmlSpecialChars(basename($file)) . ''); + } else { + return Nette\Utils\Html::el('span')->setText($file); + } + } + + + + /** + * Internal dump() implementation. + * @param mixed variable to dump + * @param int current recursion level + * @return string + */ + public static function htmlDump(&$var, $level = 0) + { + static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u'; + if ($tableUtf === NULL) { + foreach (range("\x00", "\xFF") as $ch) { + if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) { + $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT); + } elseif (ord($ch) < 127) { + $tableUtf[$ch] = $tableBin[$ch] = $ch; + } else { + $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); + } + } + $tableBin["\\"] = '\\\\'; + $tableBin["\r"] = '\\r'; + $tableBin["\n"] = '\\n'; + $tableBin["\t"] = '\\t'; + $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x'; + } + + if (is_bool($var)) { + return '' . ($var ? 'TRUE' : 'FALSE') . "\n"; + + } elseif ($var === NULL) { + return "NULL\n"; + + } elseif (is_int($var)) { + return "$var\n"; + + } elseif (is_float($var)) { + $var = var_export($var, TRUE); + if (strpos($var, '.') === FALSE) { + $var .= '.0'; + } + return "$var\n"; + + } elseif (is_string($var)) { + if (Debugger::$maxLen && strlen($var) > Debugger::$maxLen) { + $s = htmlSpecialChars(substr($var, 0, Debugger::$maxLen), ENT_NOQUOTES, 'ISO-8859-1') . ' ... '; + } else { + $s = htmlSpecialChars($var, ENT_NOQUOTES, 'ISO-8859-1'); + } + $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf); + $len = strlen($var); + return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n"; + + } elseif (is_array($var)) { + $s = 'array(' . count($var) . ") "; + $space = str_repeat($space1 = ' ', $level); + $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}"; + + static $marker; + if ($marker === NULL) { + $marker = uniqid("\x00", TRUE); + } + if (empty($var)) { + + } elseif (isset($var[$marker])) { + $brackets = $var[$marker]; + $s .= "$brackets[0] *RECURSION* $brackets[1]"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth) { + $s .= "$brackets[0]\n"; + $var[$marker] = $brackets; + foreach ($var as $k => &$v) { + if ($k === $marker) { + continue; + } + $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf); + $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\""); + $s .= "$space$space1$k => " . self::htmlDump($v, $level + 1); + } + unset($var[$marker]); + $s .= "$space$brackets[1]"; + + } else { + $s .= "$brackets[0] ... $brackets[1]"; + } + return $s . "\n"; + + } elseif (is_object($var)) { + if ($var instanceof \Closure) { + $rc = new \ReflectionFunction($var); + $arr = array(); + foreach ($rc->getParameters() as $param) { + $arr[] = '$' . $param->getName(); + } + $arr = array('file' => $rc->getFileName(), 'line' => $rc->getStartLine(), 'parameters' => implode(', ', $arr)); + } else { + $arr = (array) $var; + } + $s = '' . get_class($var) . "(" . count($arr) . ") "; + $space = str_repeat($space1 = ' ', $level); + + static $list = array(); + if (empty($arr)) { + + } elseif (in_array($var, $list, TRUE)) { + $s .= "{ *RECURSION* }"; + + } elseif ($level < Debugger::$maxDepth || !Debugger::$maxDepth || $var instanceof \Closure) { + $s .= "{\n"; + $list[] = $var; + foreach ($arr as $k => &$v) { + $m = ''; + if ($k[0] === "\x00") { + $m = ' ' . ($k[1] === '*' ? 'protected' : 'private') . ''; + $k = substr($k, strrpos($k, "\x00") + 1); + } + $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf); + $k = htmlSpecialChars(preg_match('#^\w+$#', $k) ? $k : "\"$k\""); + $s .= "$space$space1$k$m => " . self::htmlDump($v, $level + 1); + } + array_pop($list); + $s .= "$space}"; + + } else { + $s .= "{ ... }"; + } + return $s . "\n"; + + } elseif (is_resource($var)) { + $type = get_resource_type($var); + $s = '' . htmlSpecialChars($type) . " resource "; + + static $info = array('stream' => 'stream_get_meta_data', 'curl' => 'curl_getinfo'); + if (isset($info[$type])) { + $space = str_repeat($space1 = ' ', $level); + $s .= "{\n"; + foreach (call_user_func($info[$type], $var) as $k => $v) { + $s .= $space . $space1 . '' . htmlSpecialChars($k) . " => " . self::htmlDump($v, $level + 1); + } + $s .= "$space}"; + } + return $s . "\n"; + + } else { + return "unknown type\n"; + } + } + + + + /** + * Dumps variable. + * @param string + * @return string + */ + public static function clickableDump($dump, $collapsed = FALSE) + { + return '
' . preg_replace_callback(
+			'#^( *)((?>[^(\r\n]{1,200}))\((\d+)\) #m',
+			function ($m) use ($collapsed) {
+				return "$m[1]'
+					: ' ');
+			},
+			self::htmlDump($dump)
+		) . '
'; + } + + + + public static function findTrace(array $trace, $method, & $index = NULL) + { + $m = explode('::', $method); + foreach ($trace as $i => $item) { + if (isset($item['function']) && $item['function'] === end($m) + && isset($item['class']) === isset($m[1]) + && (!isset($item['class']) || $item['class'] === $m[0] || is_subclass_of($item['class'], $m[0]))) + { + $index = $i; + return $item; + } + } + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/IBarPanel.php b/apigen/libs/Nette/Nette/Diagnostics/IBarPanel.php new file mode 100644 index 00000000000..97262b59dbe --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/IBarPanel.php @@ -0,0 +1,38 @@ +directory)) { + throw new Nette\DirectoryNotFoundException("Directory '$this->directory' is not found or is not directory."); + } + + if (is_array($message)) { + $message = implode(' ', $message); + } + $res = error_log(trim($message) . PHP_EOL, 3, $this->directory . '/' . strtolower($priority) . '.log'); + + if (($priority === self::ERROR || $priority === self::CRITICAL) && $this->email && $this->mailer + && @filemtime($this->directory . '/email-sent') + self::$emailSnooze < time() // @ - file may not exist + && @file_put_contents($this->directory . '/email-sent', 'sent') // @ - file may not be writable + ) { + Nette\Callback::create($this->mailer)->invoke($message, $this->email); + } + return $res; + } + + + + /** + * Default mailer. + * @param string + * @param string + * @return void + */ + public static function defaultMailer($message, $email) + { + $host = php_uname('n'); + foreach (array('HTTP_HOST','SERVER_NAME', 'HOSTNAME') as $item) { + if (isset($_SERVER[$item])) { + $host = $_SERVER[$item]; break; + } + } + + $parts = str_replace( + array("\r\n", "\n"), + array("\n", PHP_EOL), + array( + 'headers' => implode("\n", array( + "From: noreply@$host", + 'X-Mailer: Nette Framework', + 'Content-Type: text/plain; charset=UTF-8', + 'Content-Transfer-Encoding: 8bit', + )) . "\n", + 'subject' => "PHP: An error occurred on the server $host", + 'body' => "[" . @date('Y-m-d H:i:s') . "] $message", // @ - timezone may not be set + ) + ); + + mail($email, $parts['subject'], $parts['body'], $parts['headers']); + } + +} diff --git a/apigen/libs/Nette/Nette/Diagnostics/shortcuts.php b/apigen/libs/Nette/Nette/Diagnostics/shortcuts.php new file mode 100644 index 00000000000..b18d20e17a0 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/shortcuts.php @@ -0,0 +1,25 @@ + + + + +

Dumped variables

+ +
+ + +

+ + + + + $dump): ?> + + + + + +
+ +
diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.dumps.tab.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.dumps.tab.phtml new file mode 100644 index 00000000000..72c5bcb98d8 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.dumps.tab.phtml @@ -0,0 +1,14 @@ + +variables diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.panel.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.panel.phtml new file mode 100644 index 00000000000..9dbc60465d0 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.panel.phtml @@ -0,0 +1,26 @@ + +

Errors

+ +
+ + + $count): list($message, $file, $line) = explode('|', $item) ?> + + + + + +
+
diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.tab.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.tab.phtml new file mode 100644 index 00000000000..4eb5b15f77b --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.errors.tab.phtml @@ -0,0 +1,15 @@ + + errors diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.memory.tab.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.memory.tab.phtml new file mode 100644 index 00000000000..b434cc42a47 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.memory.tab.phtml @@ -0,0 +1,15 @@ + + MB diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.phtml new file mode 100644 index 00000000000..4e1000a0dea --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.phtml @@ -0,0 +1,616 @@ + + + + + + + + +  + + + + + + + + + + $panel): if (!$panel['panel']) continue; ?> +
+
+ +
+ ¤ + × +
+
+
+ + +
+
+
    + + +
  • ', trim($panel['tab']), ''; endif ?>
  • + +
  • ×
  • +
+
+
+ + + +
+ + + + diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bar.time.tab.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.time.tab.phtml new file mode 100644 index 00000000000..4bcd33b0d51 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bar.time.tab.phtml @@ -0,0 +1,15 @@ + + ms diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/bluescreen.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/bluescreen.phtml new file mode 100644 index 00000000000..2e9cc9ea136 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/bluescreen.phtml @@ -0,0 +1,644 @@ + 'Fatal Error', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Recoverable Error', + E_CORE_ERROR => 'Core Error', + E_COMPILE_ERROR => 'Compile Error', + E_PARSE => 'Parse Error', + E_WARNING => 'Warning', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_WARNING => 'User Warning', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Strict', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', +); + +$title = ($exception instanceof Nette\FatalErrorException && isset($errorTypes[$exception->getSeverity()])) ? $errorTypes[$exception->getSeverity()] : get_class($exception); + +$expandPath = NETTE_DIR . DIRECTORY_SEPARATOR; // . 'Utils' . DIRECTORY_SEPARATOR . 'Object'; +$counter = 0; + +?>
+ + + + + + + <?php echo htmlspecialchars($title) ?> + + + + + + + +
+
+
+

getCode() ? ' #' . $exception->getCode() : '') ?>

+ +

getMessage()) ?> getMessage())) ?>" id="netteBsSearch">search►

+
+ + + + + + + +
+

Caused by 2) ? '►' : '▼' ?>

+ +
+
+

getCode() ? ' #' . $ex->getCode() : '')) ?>

+ +

getMessage()) ?>

+
+ + + + + + + +
+

+ +
+ +
+ + + + + getTrace(); $expanded = NULL ?> + getFile(), $expandPath) === 0) { + foreach ($stack as $key => $row) { + if (isset($row['file']) && strpos($row['file'], $expandPath) !== 0) { $expanded = $key; break; } + } + } ?> + +
+

Source file

+ +
+

File: getFile(), $ex->getLine()) ?>   Line: getLine() ?>

+ getFile())): ?>getFile(), $ex->getLine(), 15, isset($ex->context) ? $ex->context : NULL) ?> +
+ + + + + +
+

Call stack

+ +
+
    + $row): ?> +
  1. + + + + + inner-code + + + ">source   + + + + + (">arguments ) +

    + + +
    "> + + getParameters(); + } catch (\Exception $e) { + $params = array(); + } + foreach ($row['args'] as $k => $v) { + echo '\n"; + } + ?> +
    ', htmlspecialchars(isset($params[$k]) ? '$' . $params[$k]->name : "#$k"), ''; + echo Helpers::clickableDump($v); + echo "
    +
    + + + + +
    id="netteBsSrc">
    + + +
  2. + +
+
+ + + + + context) && is_array($ex->context)):?> +
+

Variables

+ +
+
+ + context as $k => $v) { + echo '\n"; + } + ?> +
$', htmlspecialchars($k), '', Helpers::clickableDump($v), "
+
+
+ + + getPrevious()) || (isset($ex->previous) && $ex = $ex->previous)); ?> +
' ?> + + + + + + + +
+

+ +
+ +
+ + + + +
+

Environment

+ +
+

$_SERVER

+
+ + $v) echo '\n"; + ?> +
', htmlspecialchars($k), '', Helpers::clickableDump($v), "
+
+ + +

$_SESSION

+
+ +

empty

+ + + $v) echo '\n"; + ?> +
', htmlspecialchars($k), '', $k === '__NF' ? 'Nette Session' : Helpers::clickableDump($v), "
+ +
+ + + +

Nette Session

+
+ + $v) echo '\n"; + ?> +
', htmlspecialchars($k), '', Helpers::clickableDump($v), "
+
+ + + + +

Constants

+
+ + $v) { + echo ''; + echo '\n"; + } + ?> +
', htmlspecialchars($k), '', Helpers::clickableDump($v), "
+
+ + + +

Included files ()

+
+ + \n"; + } + ?> +
', htmlspecialchars($v), "
+
+ + +

Configuration options

+
+ |.+$#s', '', ob_get_clean()) ?> +
+
+ + + +
+

HTTP request

+ +
+ +

Headers

+
+ + $v) echo '\n"; + ?> +
', htmlspecialchars($k), '', htmlspecialchars($v), "
+
+ + + + +

$

+ +

empty

+ +
+ + $v) echo '\n"; + ?> +
', htmlspecialchars($k), '', Helpers::clickableDump($v), "
+
+ + +
+ + + +
+

HTTP response

+ +
+

Headers

+ +
';
+			?>
+ +

no headers

+ +
+ + + + +
+

+ +
+ +
+ + + + +
    +
  • Report generated at
  • + +
  • + +
  • + +
  • PHP
  • +
  • +
  • (revision )
  • +
+
+
+ + + + diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/error.phtml b/apigen/libs/Nette/Nette/Diagnostics/templates/error.phtml new file mode 100644 index 00000000000..9b0f1d9daef --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/error.phtml @@ -0,0 +1,26 @@ + + + + + + +Server Error + +

Server Error

+ +

We're sorry! The server encountered an internal error and was unable to complete your request. Please try again later.

+ +

error 500

diff --git a/apigen/libs/Nette/Nette/Diagnostics/templates/netteQ.js b/apigen/libs/Nette/Nette/Diagnostics/templates/netteQ.js new file mode 100644 index 00000000000..68cf3a45622 --- /dev/null +++ b/apigen/libs/Nette/Nette/Diagnostics/templates/netteQ.js @@ -0,0 +1,332 @@ +/** + * NetteQ + * + * This file is part of the Nette Framework. + * Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com) + */ + +var Nette = Nette || {}; + +(function(){ + +// simple class builder +Nette.Class = function(def) { + var cl = def.constructor || function(){}, nm, __hasProp = Object.prototype.hasOwnProperty; + delete def.constructor; + + if (def.Extends) { + var foo = function() { this.constructor = cl; }; + foo.prototype = def.Extends.prototype; + cl.prototype = new foo(); + delete def.Extends; + } + + if (def.Static) { + for (nm in def.Static) { if (__hasProp.call(def.Static, nm)) cl[nm] = def.Static[nm]; } + delete def.Static; + } + + for (nm in def) { if (__hasProp.call(def, nm)) cl.prototype[nm] = def[nm]; } + return cl; +}; + + +// supported cross-browser selectors: #id | div | div.class | .class +Nette.Q = Nette.Class({ + + Static: { + factory: function(selector) { + return new Nette.Q(selector); + }, + + implement: function(methods) { + var nm, fn = Nette.Q.implement, prot = Nette.Q.prototype, __hasProp = Object.prototype.hasOwnProperty; + for (nm in methods) { + if (!__hasProp.call(methods, nm)) { + continue; + } + fn[nm] = methods[nm]; + prot[nm] = (function(nm){ + return function() { return this.each(fn[nm], arguments); }; + }(nm)); + } + } + }, + + constructor: function(selector) { + if (typeof selector === "string") { + selector = this._find(document, selector); + + } else if (!selector || selector.nodeType || selector.length === undefined || selector === window) { + selector = [selector]; + } + + for (var i = 0, len = selector.length; i < len; i++) { + if (selector[i]) { this[this.length++] = selector[i]; } + } + }, + + length: 0, + + find: function(selector) { + return new Nette.Q(this._find(this[0], selector)); + }, + + _find: function(context, selector) { + if (!context || !selector) { + return []; + + } else if (document.querySelectorAll) { + return context.querySelectorAll(selector); + + } else if (selector.charAt(0) === '#') { // #id + return [document.getElementById(selector.substring(1))]; + + } else { // div | div.class | .class + selector = selector.split('.'); + var elms = context.getElementsByTagName(selector[0] || '*'); + + if (selector[1]) { + var list = [], pattern = new RegExp('(^|\\s)' + selector[1] + '(\\s|$)'); + for (var i = 0, len = elms.length; i < len; i++) { + if (pattern.test(elms[i].className)) { list.push(elms[i]); } + } + return list; + } else { + return elms; + } + } + }, + + dom: function() { + return this[0]; + }, + + each: function(callback, args) { + for (var i = 0, res; i < this.length; i++) { + if ((res = callback.apply(this[i], args || [])) !== undefined) { return res; } + } + return this; + } +}); + + +var $ = Nette.Q.factory, fn = Nette.Q.implement; + +fn({ + // cross-browser event attach + bind: function(event, handler) { + if (document.addEventListener && (event === 'mouseenter' || event === 'mouseleave')) { // simulate mouseenter & mouseleave using mouseover & mouseout + var old = handler; + event = event === 'mouseenter' ? 'mouseover' : 'mouseout'; + handler = function(e) { + for (var target = e.relatedTarget; target; target = target.parentNode) { + if (target === this) { return; } // target must not be inside this + } + old.call(this, e); + }; + } + + var data = fn.data.call(this), + events = data.events = data.events || {}; // use own handler queue + + if (!events[event]) { + var el = this, // fixes 'this' in iE + handlers = events[event] = [], + generic = fn.bind.genericHandler = function(e) { // dont worry, 'e' is passed in IE + if (!e.target) { + e.target = e.srcElement; + } + if (!e.preventDefault) { + e.preventDefault = function() { e.returnValue = false; }; + } + if (!e.stopPropagation) { + e.stopPropagation = function() { e.cancelBubble = true; }; + } + e.stopImmediatePropagation = function() { this.stopPropagation(); i = handlers.length; }; + for (var i = 0; i < handlers.length; i++) { + handlers[i].call(el, e); + } + }; + + if (document.addEventListener) { // non-IE + this.addEventListener(event, generic, false); + } else if (document.attachEvent) { // IE < 9 + this.attachEvent('on' + event, generic); + } + } + + events[event].push(handler); + }, + + // adds class to element + addClass: function(className) { + this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' ') + ' ' + className; + }, + + // removes class from element + removeClass: function(className) { + this.className = this.className.replace(/^|\s+|$/g, ' ').replace(' '+className+' ', ' '); + }, + + // tests whether element has given class + hasClass: function(className) { + return this.className.replace(/^|\s+|$/g, ' ').indexOf(' '+className+' ') > -1; + }, + + show: function() { + var dsp = fn.show.display = fn.show.display || {}, tag = this.tagName; + if (!dsp[tag]) { + var el = document.body.appendChild(document.createElement(tag)); + dsp[tag] = fn.css.call(el, 'display'); + } + this.style.display = dsp[tag]; + }, + + hide: function() { + this.style.display = 'none'; + }, + + css: function(property) { + return this.currentStyle ? this.currentStyle[property] + : (window.getComputedStyle ? document.defaultView.getComputedStyle(this, null).getPropertyValue(property) : undefined); + }, + + data: function() { + return this.nette ? this.nette : this.nette = {}; + }, + + val: function() { + var i; + if (!this.nodeName) { // radio + for (i = 0, len = this.length; i < len; i++) { + if (this[i].checked) { return this[i].value; } + } + return null; + } + + if (this.nodeName.toLowerCase() === 'select') { + var index = this.selectedIndex, options = this.options; + + if (index < 0) { + return null; + + } else if (this.type === 'select-one') { + return options[index].value; + } + + for (i = 0, values = [], len = options.length; i < len; i++) { + if (options[i].selected) { values.push(options[i].value); } + } + return values; + } + + if (this.type === 'checkbox') { + return this.checked; + } + + return this.value.replace(/^\s+|\s+$/g, ''); + }, + + _trav: function(el, selector, fce) { + selector = selector.split('.'); + while (el && !(el.nodeType === 1 && + (!selector[0] || el.tagName.toLowerCase() === selector[0]) && + (!selector[1] || fn.hasClass.call(el, selector[1])))) { + el = el[fce]; + } + return $(el); + }, + + closest: function(selector) { + return fn._trav(this, selector, 'parentNode'); + }, + + prev: function(selector) { + return fn._trav(this.previousSibling, selector, 'previousSibling'); + }, + + next: function(selector) { + return fn._trav(this.nextSibling, selector, 'nextSibling'); + }, + + // returns total offset for element + offset: function(coords) { + var el = this, ofs = coords ? {left: -coords.left || 0, top: -coords.top || 0} : fn.position.call(el); + while (el = el.offsetParent) { ofs.left += el.offsetLeft; ofs.top += el.offsetTop; } + + if (coords) { + fn.position.call(this, {left: -ofs.left, top: -ofs.top}); + } else { + return ofs; + } + }, + + // returns current position or move to new position + position: function(coords) { + if (coords) { + if (this.nette && this.nette.onmove) { + this.nette.onmove.call(this, coords); + } + this.style.left = (coords.left || 0) + 'px'; + this.style.top = (coords.top || 0) + 'px'; + } else { + return {left: this.offsetLeft, top: this.offsetTop, width: this.offsetWidth, height: this.offsetHeight}; + } + }, + + // makes element draggable + draggable: function(options) { + var $el = $(this), dE = document.documentElement, started; + options = options || {}; + + $(options.handle || this).bind('mousedown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + if (fn.draggable.binded) { // missed mouseup out of window? + return dE.onmouseup(e); + } + + var deltaX = $el[0].offsetLeft - e.clientX, deltaY = $el[0].offsetTop - e.clientY; + fn.draggable.binded = true; + started = false; + + dE.onmousemove = function(e) { + e = e || event; + if (!started) { + if (options.draggedClass) { + $el.addClass(options.draggedClass); + } + if (options.start) { + options.start(e, $el); + } + started = true; + } + $el.position({left: e.clientX + deltaX, top: e.clientY + deltaY}); + return false; + }; + + dE.onmouseup = function(e) { + if (started) { + if (options.draggedClass) { + $el.removeClass(options.draggedClass); + } + if (options.stop) { + options.stop(e || event, $el); + } + } + fn.draggable.binded = dE.onmousemove = dE.onmouseup = null; + return false; + }; + + }).bind('click', function(e) { + if (started) { + e.stopImmediatePropagation(); + preventClick = false; + } + }); + } +}); + +})(); diff --git a/apigen/libs/Nette/Nette/Forms/Container.php b/apigen/libs/Nette/Nette/Forms/Container.php new file mode 100644 index 00000000000..3ce3bbf3275 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Container.php @@ -0,0 +1,493 @@ +getForm(FALSE); + if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { + $this->setValues($values, $erase); + } + return $this; + } + + + + /** + * Fill-in with values. + * @param array|Traversable values used to fill the form + * @param bool erase other controls? + * @return Container provides a fluent interface + */ + public function setValues($values, $erase = FALSE) + { + if ($values instanceof \Traversable) { + $values = iterator_to_array($values); + + } elseif (!is_array($values)) { + throw new Nette\InvalidArgumentException("First parameter must be an array, " . gettype($values) ." given."); + } + + foreach ($this->getComponents() as $name => $control) { + if ($control instanceof IControl) { + if (array_key_exists($name, $values)) { + $control->setValue($values[$name]); + + } elseif ($erase) { + $control->setValue(NULL); + } + + } elseif ($control instanceof Container) { + if (array_key_exists($name, $values)) { + $control->setValues($values[$name], $erase); + + } elseif ($erase) { + $control->setValues(array(), $erase); + } + } + } + return $this; + } + + + + /** + * Returns the values submitted by the form. + * @param bool return values as an array? + * @return Nette\ArrayHash|array + */ + public function getValues($asArray = FALSE) + { + $values = $asArray ? array() : new Nette\ArrayHash; + foreach ($this->getComponents() as $name => $control) { + if ($control instanceof IControl && !$control->isDisabled() && !$control instanceof ISubmitterControl) { + $values[$name] = $control->getValue(); + + } elseif ($control instanceof Container) { + $values[$name] = $control->getValues($asArray); + } + } + return $values; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Is form valid? + * @return bool + */ + public function isValid() + { + if ($this->valid === NULL) { + $this->validate(); + } + return $this->valid; + } + + + + /** + * Performs the server side validation. + * @return void + */ + public function validate() + { + $this->valid = TRUE; + $this->onValidate($this); + foreach ($this->getControls() as $control) { + if (!$control->getRules()->validate()) { + $this->valid = FALSE; + } + } + } + + + + /********************* form building ****************d*g**/ + + + + /** + * @param ControlGroup + * @return Container provides a fluent interface + */ + public function setCurrentGroup(ControlGroup $group = NULL) + { + $this->currentGroup = $group; + return $this; + } + + + + /** + * Returns current group. + * @return ControlGroup + */ + public function getCurrentGroup() + { + return $this->currentGroup; + } + + + + /** + * Adds the specified component to the IContainer. + * @param IComponent + * @param string + * @param string + * @return Container provides a fluent interface + * @throws Nette\InvalidStateException + */ + public function addComponent(Nette\ComponentModel\IComponent $component, $name, $insertBefore = NULL) + { + parent::addComponent($component, $name, $insertBefore); + if ($this->currentGroup !== NULL && $component instanceof IControl) { + $this->currentGroup->add($component); + } + return $this; + } + + + + /** + * Iterates over all form controls. + * @return \ArrayIterator + */ + public function getControls() + { + return $this->getComponents(TRUE, 'Nette\Forms\IControl'); + } + + + + /** + * Returns form. + * @param bool throw exception if form doesn't exist? + * @return Form + */ + public function getForm($need = TRUE) + { + return $this->lookup('Nette\Forms\Form', $need); + } + + + + /********************* control factories ****************d*g**/ + + + + /** + * Adds single-line text input control to the form. + * @param string control name + * @param string label + * @param int width of the control + * @param int maximum number of characters the user may enter + * @return Nette\Forms\Controls\TextInput + */ + public function addText($name, $label = NULL, $cols = NULL, $maxLength = NULL) + { + return $this[$name] = new Controls\TextInput($label, $cols, $maxLength); + } + + + + /** + * Adds single-line text input control used for sensitive input such as passwords. + * @param string control name + * @param string label + * @param int width of the control + * @param int maximum number of characters the user may enter + * @return Nette\Forms\Controls\TextInput + */ + public function addPassword($name, $label = NULL, $cols = NULL, $maxLength = NULL) + { + $control = new Controls\TextInput($label, $cols, $maxLength); + $control->setType('password'); + return $this[$name] = $control; + } + + + + /** + * Adds multi-line text input control to the form. + * @param string control name + * @param string label + * @param int width of the control + * @param int height of the control in text lines + * @return Nette\Forms\Controls\TextArea + */ + public function addTextArea($name, $label = NULL, $cols = 40, $rows = 10) + { + return $this[$name] = new Controls\TextArea($label, $cols, $rows); + } + + + + /** + * Adds control that allows the user to upload files. + * @param string control name + * @param string label + * @return Nette\Forms\Controls\UploadControl + */ + public function addUpload($name, $label = NULL) + { + return $this[$name] = new Controls\UploadControl($label); + } + + + + /** + * Adds hidden form control used to store a non-displayed value. + * @param string control name + * @param mixed default value + * @return Nette\Forms\Controls\HiddenField + */ + public function addHidden($name, $default = NULL) + { + $control = new Controls\HiddenField; + $control->setDefaultValue($default); + return $this[$name] = $control; + } + + + + /** + * Adds check box control to the form. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\Checkbox + */ + public function addCheckbox($name, $caption = NULL) + { + return $this[$name] = new Controls\Checkbox($caption); + } + + + + /** + * Adds set of radio button controls to the form. + * @param string control name + * @param string label + * @param array options from which to choose + * @return Nette\Forms\Controls\RadioList + */ + public function addRadioList($name, $label = NULL, array $items = NULL) + { + return $this[$name] = new Controls\RadioList($label, $items); + } + + + + /** + * Adds select box control that allows single item selection. + * @param string control name + * @param string label + * @param array items from which to choose + * @param int number of rows that should be visible + * @return Nette\Forms\Controls\SelectBox + */ + public function addSelect($name, $label = NULL, array $items = NULL, $size = NULL) + { + return $this[$name] = new Controls\SelectBox($label, $items, $size); + } + + + + /** + * Adds select box control that allows multiple item selection. + * @param string control name + * @param string label + * @param array options from which to choose + * @param int number of rows that should be visible + * @return Nette\Forms\Controls\MultiSelectBox + */ + public function addMultiSelect($name, $label = NULL, array $items = NULL, $size = NULL) + { + return $this[$name] = new Controls\MultiSelectBox($label, $items, $size); + } + + + + /** + * Adds button used to submit form. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\SubmitButton + */ + public function addSubmit($name, $caption = NULL) + { + return $this[$name] = new Controls\SubmitButton($caption); + } + + + + /** + * Adds push buttons with no default behavior. + * @param string control name + * @param string caption + * @return Nette\Forms\Controls\Button + */ + public function addButton($name, $caption) + { + return $this[$name] = new Controls\Button($caption); + } + + + + /** + * Adds graphical button used to submit form. + * @param string control name + * @param string URI of the image + * @param string alternate text for the image + * @return Nette\Forms\Controls\ImageButton + */ + public function addImage($name, $src = NULL, $alt = NULL) + { + return $this[$name] = new Controls\ImageButton($src, $alt); + } + + + + /** + * Adds naming container to the form. + * @param string name + * @return Container + */ + public function addContainer($name) + { + $control = new Container; + $control->currentGroup = $this->currentGroup; + return $this[$name] = $control; + } + + + + /********************* interface \ArrayAccess ****************d*g**/ + + + + /** + * Adds the component to the container. + * @param string component name + * @param Nette\ComponentModel\IComponent + * @return void + */ + final public function offsetSet($name, $component) + { + $this->addComponent($component, $name); + } + + + + /** + * Returns component specified by name. Throws exception if component doesn't exist. + * @param string component name + * @return Nette\ComponentModel\IComponent + * @throws Nette\InvalidArgumentException + */ + final public function offsetGet($name) + { + return $this->getComponent($name, TRUE); + } + + + + /** + * Does component specified by name exists? + * @param string component name + * @return bool + */ + final public function offsetExists($name) + { + return $this->getComponent($name, FALSE) !== NULL; + } + + + + /** + * Removes component from the container. + * @param string component name + * @return void + */ + final public function offsetUnset($name) + { + $component = $this->getComponent($name, FALSE); + if ($component !== NULL) { + $this->removeComponent($component); + } + } + + + + /** + * Prevents cloning. + */ + final public function __clone() + { + throw new Nette\NotImplementedException('Form cloning is not supported yet.'); + } + + + + /********************* deprecated ****************d*g**/ + + /** @deprecated */ + function addFile($name, $label = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use addUpload() instead.', E_USER_WARNING); + return $this->addUpload($name, $label); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/ControlGroup.php b/apigen/libs/Nette/Nette/Forms/ControlGroup.php new file mode 100644 index 00000000000..730d846e47d --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/ControlGroup.php @@ -0,0 +1,124 @@ +controls = new \SplObjectStorage; + } + + + + /** + * @return ControlGroup provides a fluent interface + */ + public function add() + { + foreach (func_get_args() as $num => $item) { + if ($item instanceof IControl) { + $this->controls->attach($item); + + } elseif ($item instanceof \Traversable || is_array($item)) { + foreach ($item as $control) { + $this->controls->attach($control); + } + + } else { + throw new Nette\InvalidArgumentException("Only IFormControl items are allowed, the #$num parameter is invalid."); + } + } + return $this; + } + + + + /** + * @return array IFormControl + */ + public function getControls() + { + return iterator_to_array($this->controls); + } + + + + /** + * Sets user-specific option. + * Options recognized by DefaultFormRenderer + * - 'label' - textual or Html object label + * - 'visual' - indicates visual group + * - 'container' - container as Html object + * - 'description' - textual or Html object description + * - 'embedNext' - describes how render next group + * + * @param string key + * @param mixed value + * @return ControlGroup provides a fluent interface + */ + public function setOption($key, $value) + { + if ($value === NULL) { + unset($this->options[$key]); + + } else { + $this->options[$key] = $value; + } + return $this; + } + + + + /** + * Returns user-specific option. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getOption($key, $default = NULL) + { + return isset($this->options[$key]) ? $this->options[$key] : $default; + } + + + + /** + * Returns user-specific options. + * @return array + */ + final public function getOptions() + { + return $this->options; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/BaseControl.php b/apigen/libs/Nette/Nette/Forms/Controls/BaseControl.php new file mode 100644 index 00000000000..665668abf94 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/BaseControl.php @@ -0,0 +1,666 @@ +monitor('Nette\Forms\Form'); + parent::__construct(); + $this->control = Html::el('input'); + $this->label = Html::el('label'); + $this->caption = $caption; + $this->rules = new Nette\Forms\Rules($this); + } + + + + /** + * This method will be called when the component becomes attached to Form. + * @param Nette\Forms\IComponent + * @return void + */ + protected function attached($form) + { + if (!$this->disabled && $form instanceof Form && $form->isAnchored() && $form->isSubmitted()) { + $this->htmlName = NULL; + $this->loadHttpData(); + } + } + + + + /** + * Returns form. + * @param bool throw exception if form doesn't exist? + * @return Nette\Forms\Form + */ + public function getForm($need = TRUE) + { + return $this->lookup('Nette\Forms\Form', $need); + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + if ($this->htmlName === NULL) { + $name = str_replace(self::NAME_SEPARATOR, '][', $this->lookupPath('Nette\Forms\Form'), $count); + if ($count) { + $name = substr_replace($name, '', strpos($name, ']'), 1) . ']'; + } + if (is_numeric($name) || in_array($name, array('attributes','children','elements','focus','length','reset','style','submit','onsubmit'))) { + $name .= '_'; + } + $this->htmlName = $name; + } + return $this->htmlName; + } + + + + /** + * Changes control's HTML id. + * @param string new ID, or FALSE or NULL + * @return BaseControl provides a fluent interface + */ + public function setHtmlId($id) + { + $this->htmlId = $id; + return $this; + } + + + + /** + * Returns control's HTML id. + * @return string + */ + public function getHtmlId() + { + if ($this->htmlId === FALSE) { + return NULL; + + } elseif ($this->htmlId === NULL) { + $this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->lookupPath('Nette\Forms\Form')); + } + return $this->htmlId; + } + + + + /** + * Changes control's HTML attribute. + * @param string name + * @param mixed value + * @return BaseControl provides a fluent interface + */ + public function setAttribute($name, $value = TRUE) + { + $this->control->$name = $value; + return $this; + } + + + + /** + * Sets user-specific option. + * Options recognized by DefaultFormRenderer + * - 'description' - textual or Html object description + * + * @param string key + * @param mixed value + * @return BaseControl provides a fluent interface + */ + public function setOption($key, $value) + { + if ($value === NULL) { + unset($this->options[$key]); + + } else { + $this->options[$key] = $value; + } + return $this; + } + + + + /** + * Returns user-specific option. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getOption($key, $default = NULL) + { + return isset($this->options[$key]) ? $this->options[$key] : $default; + } + + + + /** + * Returns user-specific options. + * @return array + */ + final public function getOptions() + { + return $this->options; + } + + + + /********************* translator ****************d*g**/ + + + + /** + * Sets translate adapter. + * @param Nette\Localization\ITranslator + * @return BaseControl provides a fluent interface + */ + public function setTranslator(Nette\Localization\ITranslator $translator = NULL) + { + $this->translator = $translator; + return $this; + } + + + + /** + * Returns translate adapter. + * @return Nette\Localization\ITranslator|NULL + */ + final public function getTranslator() + { + if ($this->translator === TRUE) { + return $this->getForm(FALSE) ? $this->getForm()->getTranslator() : NULL; + } + return $this->translator; + } + + + + /** + * Returns translated string. + * @param string + * @param int plural count + * @return string + */ + public function translate($s, $count = NULL) + { + $translator = $this->getTranslator(); + return $translator === NULL || $s == NULL ? $s : $translator->translate($s, $count); // intentionally == + } + + + + /********************* interface IFormControl ****************d*g**/ + + + + /** + * Sets control's value. + * @param mixed + * @return BaseControl provides a fluent interface + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } + + + + /** + * Returns control's value. + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + + + /** + * Is control filled? + * @return bool + */ + public function isFilled() + { + return (string) $this->getValue() !== ''; // NULL, FALSE, '' ==> FALSE + } + + + + /** + * Sets control's default value. + * @param mixed + * @return BaseControl provides a fluent interface + */ + public function setDefaultValue($value) + { + $form = $this->getForm(FALSE); + if (!$form || !$form->isAnchored() || !$form->isSubmitted()) { + $this->setValue($value); + } + return $this; + } + + + + /** + * Loads HTTP data. + * @return void + */ + public function loadHttpData() + { + $path = explode('[', strtr(str_replace(array('[]', ']'), '', $this->getHtmlName()), '.', '_')); + $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL)); + } + + + + /** + * Disables or enables control. + * @param bool + * @return BaseControl provides a fluent interface + */ + public function setDisabled($value = TRUE) + { + $this->disabled = (bool) $value; + return $this; + } + + + + /** + * Is control disabled? + * @return bool + */ + public function isDisabled() + { + return $this->disabled; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $this->setOption('rendered', TRUE); + + $control = clone $this->control; + $control->name = $this->getHtmlName(); + $control->disabled = $this->disabled; + $control->id = $this->getHtmlId(); + $control->required = $this->isRequired(); + + $rules = self::exportRules($this->rules); + $rules = substr(PHP_VERSION_ID >= 50400 ? json_encode($rules, JSON_UNESCAPED_UNICODE) : json_encode($rules), 1, -1); + $rules = preg_replace('#"([a-z0-9_]+)":#i', '$1:', $rules); + $rules = preg_replace('#(?data('nette-rules', $rules ? $rules : NULL); + + return $control; + } + + + + /** + * Generates label's HTML element. + * @param string + * @return Nette\Utils\Html + */ + public function getLabel($caption = NULL) + { + $label = clone $this->label; + $label->for = $this->getHtmlId(); + if ($caption !== NULL) { + $label->setText($this->translate($caption)); + + } elseif ($this->caption instanceof Html) { + $label->add($this->caption); + + } else { + $label->setText($this->translate($this->caption)); + } + return $label; + } + + + + /** + * Returns control's HTML element template. + * @return Nette\Utils\Html + */ + final public function getControlPrototype() + { + return $this->control; + } + + + + /** + * Returns label's HTML element template. + * @return Nette\Utils\Html + */ + final public function getLabelPrototype() + { + return $this->label; + } + + + + /********************* rules ****************d*g**/ + + + + /** + * Adds a validation rule. + * @param mixed rule type + * @param string message to display for invalid data + * @param mixed optional rule arguments + * @return BaseControl provides a fluent interface + */ + public function addRule($operation, $message = NULL, $arg = NULL) + { + $this->rules->addRule($operation, $message, $arg); + return $this; + } + + + + /** + * Adds a validation condition a returns new branch. + * @param mixed condition type + * @param mixed optional condition arguments + * @return Nette\Forms\Rules new branch + */ + public function addCondition($operation, $value = NULL) + { + return $this->rules->addCondition($operation, $value); + } + + + + /** + * Adds a validation condition based on another control a returns new branch. + * @param Nette\Forms\IControl form control + * @param mixed condition type + * @param mixed optional condition arguments + * @return Nette\Forms\Rules new branch + */ + public function addConditionOn(IControl $control, $operation, $value = NULL) + { + return $this->rules->addConditionOn($control, $operation, $value); + } + + + + /** + * @return Nette\Forms\Rules + */ + final public function getRules() + { + return $this->rules; + } + + + + /** + * Makes control mandatory. + * @param string error message + * @return BaseControl provides a fluent interface + */ + final public function setRequired($message = NULL) + { + return $this->addRule(Form::FILLED, $message); + } + + + + /** + * Is control mandatory? + * @return bool + */ + final public function isRequired() + { + foreach ($this->rules as $rule) { + if ($rule->type === Rule::VALIDATOR && !$rule->isNegative && $rule->operation === Form::FILLED) { + return TRUE; + } + } + return FALSE; + } + + + + /** + * @return array + */ + protected static function exportRules($rules) + { + $payload = array(); + foreach ($rules as $rule) { + if (!is_string($op = $rule->operation)) { + $op = new Nette\Callback($op); + if (!$op->isStatic()) { + continue; + } + } + if ($rule->type === Rule::VALIDATOR) { + $item = array('op' => ($rule->isNegative ? '~' : '') . $op, 'msg' => $rules->formatMessage($rule, FALSE)); + + } elseif ($rule->type === Rule::CONDITION) { + $item = array( + 'op' => ($rule->isNegative ? '~' : '') . $op, + 'rules' => self::exportRules($rule->subRules), + 'control' => $rule->control->getHtmlName() + ); + if ($rule->subRules->getToggles()) { + $item['toggle'] = $rule->subRules->getToggles(); + } + } + + if (is_array($rule->arg)) { + foreach ($rule->arg as $key => $value) { + $item['arg'][$key] = $value instanceof IControl ? (object) array('control' => $value->getHtmlName()) : $value; + } + } elseif ($rule->arg !== NULL) { + $item['arg'] = $rule->arg instanceof IControl ? (object) array('control' => $rule->arg->getHtmlName()) : $rule->arg; + } + + $payload[] = $item; + } + return $payload; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Equal validator: are control's value and second parameter equal? + * @param Nette\Forms\IControl + * @param mixed + * @return bool + */ + public static function validateEqual(IControl $control, $arg) + { + $value = $control->getValue(); + foreach ((is_array($value) ? $value : array($value)) as $val) { + foreach ((is_array($arg) ? $arg : array($arg)) as $item) { + if ((string) $val === (string) ($item instanceof IControl ? $item->value : $item)) { + return TRUE; + } + } + } + return FALSE; + } + + + + /** + * Filled validator: is control filled? + * @param Nette\Forms\IControl + * @return bool + */ + public static function validateFilled(IControl $control) + { + return $control->isFilled(); + } + + + + /** + * Valid validator: is control valid? + * @param Nette\Forms\IControl + * @return bool + */ + public static function validateValid(IControl $control) + { + return $control->rules->validate(TRUE); + } + + + + /** + * Adds error message to the list. + * @param string error message + * @return void + */ + public function addError($message) + { + if (!in_array($message, $this->errors, TRUE)) { + $this->errors[] = $message; + } + $this->getForm()->addError($message); + } + + + + /** + * Returns errors corresponding to control. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + + + /** + * @return bool + */ + public function hasErrors() + { + return (bool) $this->errors; + } + + + + /** + * @return void + */ + public function cleanErrors() + { + $this->errors = array(); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/Button.php b/apigen/libs/Nette/Nette/Forms/Controls/Button.php new file mode 100644 index 00000000000..aed3b48f8d8 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/Button.php @@ -0,0 +1,60 @@ +control->type = 'button'; + } + + + + /** + * Bypasses label generation. + * @return void + */ + public function getLabel($caption = NULL) + { + return NULL; + } + + + + /** + * Generates control's HTML element. + * @param string + * @return Nette\Utils\Html + */ + public function getControl($caption = NULL) + { + $control = parent::getControl(); + $control->value = $this->translate($caption === NULL ? $this->caption : $caption); + return $control; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/Checkbox.php b/apigen/libs/Nette/Nette/Forms/Controls/Checkbox.php new file mode 100644 index 00000000000..049bffde112 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/Checkbox.php @@ -0,0 +1,60 @@ +control->type = 'checkbox'; + $this->value = FALSE; + } + + + + /** + * Sets control's value. + * @param bool + * @return Checkbox provides a fluent interface + */ + public function setValue($value) + { + $this->value = is_scalar($value) ? (bool) $value : FALSE; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl()->checked($this->value); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/HiddenField.php b/apigen/libs/Nette/Nette/Forms/Controls/HiddenField.php new file mode 100644 index 00000000000..8ba7f932cb1 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/HiddenField.php @@ -0,0 +1,75 @@ +control->type = 'hidden'; + $this->value = (string) $forcedValue; + $this->forcedValue = $forcedValue; + } + + + + /** + * Bypasses label generation. + * @return void + */ + public function getLabel($caption = NULL) + { + return NULL; + } + + + + /** + * Sets control's value. + * @param string + * @return HiddenField provides a fluent interface + */ + public function setValue($value) + { + $this->value = is_scalar($value) ? (string) $value : ''; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl() + ->value($this->forcedValue === NULL ? $this->value : $this->forcedValue) + ->data('nette-rules', NULL); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/ImageButton.php b/apigen/libs/Nette/Nette/Forms/Controls/ImageButton.php new file mode 100644 index 00000000000..2f93ee9ae61 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/ImageButton.php @@ -0,0 +1,63 @@ +control->type = 'image'; + $this->control->src = $src; + $this->control->alt = $alt; + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + $name = parent::getHtmlName(); + return strpos($name, '[') === FALSE ? $name : $name . '[]'; + } + + + + /** + * Loads HTTP data. + * @return void + */ + public function loadHttpData() + { + $path = $this->getHtmlName(); // img_x or img['x'] + $path = explode('[', strtr(str_replace(']', '', strpos($path, '[') === FALSE ? $path . '.x' : substr($path, 0, -2)), '.', '_')); + $this->setValue(Nette\Utils\Arrays::get($this->getForm()->getHttpData(), $path, NULL)); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/MultiSelectBox.php b/apigen/libs/Nette/Nette/Forms/Controls/MultiSelectBox.php new file mode 100644 index 00000000000..9c39de9887e --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/MultiSelectBox.php @@ -0,0 +1,110 @@ +getRawValue(), array_keys($this->allowed)); + } + + + + /** + * Returns selected keys (not checked). + * @return array + */ + public function getRawValue() + { + if (is_scalar($this->value)) { + return array($this->value); + + } else { + $res = array(); + foreach ((array) $this->value as $val) { + if (is_scalar($val)) { + $res[] = $val; + } + } + return $res; + } + } + + + + /** + * Returns selected values. + * @return array + */ + public function getSelectedItem() + { + return $this->areKeysUsed() + ? array_intersect_key($this->allowed, array_flip($this->getValue())) + : $this->getValue(); + } + + + + /** + * Returns HTML name of control. + * @return string + */ + public function getHtmlName() + { + return parent::getHtmlName() . '[]'; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + return parent::getControl()->multiple(TRUE); + } + + + + /** + * Count/length validator. + * @param MultiSelectBox + * @param array min and max length pair + * @return bool + */ + public static function validateLength(MultiSelectBox $control, $range) + { + if (!is_array($range)) { + $range = array($range, $range); + } + $count = count($control->getSelectedItem()); + return ($range[0] === NULL || $count >= $range[0]) && ($range[1] === NULL || $count <= $range[1]); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/RadioList.php b/apigen/libs/Nette/Nette/Forms/Controls/RadioList.php new file mode 100644 index 00000000000..4fb2fffe619 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/RadioList.php @@ -0,0 +1,190 @@ +control->type = 'radio'; + $this->container = Html::el(); + $this->separator = Html::el('br'); + if ($items !== NULL) { + $this->setItems($items); + } + } + + + + /** + * Returns selected radio value. + * @param bool + * @return mixed + */ + public function getValue($raw = FALSE) + { + return is_scalar($this->value) && ($raw || isset($this->items[$this->value])) ? $this->value : NULL; + } + + + + /** + * Has been any radio button selected? + * @return bool + */ + public function isFilled() + { + return $this->getValue() !== NULL; + } + + + + /** + * Sets options from which to choose. + * @param array + * @return RadioList provides a fluent interface + */ + public function setItems(array $items) + { + $this->items = $items; + return $this; + } + + + + /** + * Returns options from which to choose. + * @return array + */ + final public function getItems() + { + return $this->items; + } + + + + /** + * Returns separator HTML element template. + * @return Nette\Utils\Html + */ + final public function getSeparatorPrototype() + { + return $this->separator; + } + + + + /** + * Returns container HTML element template. + * @return Nette\Utils\Html + */ + final public function getContainerPrototype() + { + return $this->container; + } + + + + /** + * Generates control's HTML element. + * @param mixed + * @return Nette\Utils\Html + */ + public function getControl($key = NULL) + { + if ($key === NULL) { + $container = clone $this->container; + $separator = (string) $this->separator; + + } elseif (!isset($this->items[$key])) { + return NULL; + } + + $control = parent::getControl(); + $id = $control->id; + $counter = -1; + $value = $this->value === NULL ? NULL : (string) $this->getValue(); + $label = Html::el('label'); + + foreach ($this->items as $k => $val) { + $counter++; + if ($key !== NULL && (string) $key !== (string) $k) { + continue; + } + + $control->id = $label->for = $id . '-' . $counter; + $control->checked = (string) $k === $value; + $control->value = $k; + + if ($val instanceof Html) { + $label->setHtml($val); + } else { + $label->setText($this->translate((string) $val)); + } + + if ($key !== NULL) { + return Html::el()->add($control)->add($label); + } + + $container->add((string) $control . (string) $label . $separator); + $control->data('nette-rules', NULL); + // TODO: separator after last item? + } + + return $container; + } + + + + /** + * Generates label's HTML element. + * @param string + * @return void + */ + public function getLabel($caption = NULL) + { + $label = parent::getLabel($caption); + $label->for = NULL; + return $label; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/SelectBox.php b/apigen/libs/Nette/Nette/Forms/Controls/SelectBox.php new file mode 100644 index 00000000000..25634caf8df --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/SelectBox.php @@ -0,0 +1,242 @@ +control->setName('select'); + $this->control->size = $size > 1 ? (int) $size : NULL; + if ($items !== NULL) { + $this->setItems($items); + } + } + + + + /** + * Returns selected item key. + * @return mixed + */ + public function getValue() + { + return is_scalar($this->value) && isset($this->allowed[$this->value]) ? $this->value : NULL; + } + + + + /** + * Returns selected item key (not checked). + * @return mixed + */ + public function getRawValue() + { + return is_scalar($this->value) ? $this->value : NULL; + } + + + + /** + * Has been any item selected? + * @return bool + */ + public function isFilled() + { + $value = $this->getValue(); + return is_array($value) ? count($value) > 0 : $value !== NULL; + } + + + + /** + * Sets first prompt item in select box. + * @param string + * @return SelectBox provides a fluent interface + */ + public function setPrompt($prompt) + { + if ($prompt === TRUE) { // back compatibility + $prompt = reset($this->items); + unset($this->allowed[key($this->items)], $this->items[key($this->items)]); + } + $this->prompt = $prompt; + return $this; + } + + + + /** @deprecated */ + function skipFirst($v = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use setPrompt() instead.', E_USER_WARNING); + return $this->setPrompt($v); + } + + + + /** + * Returns first prompt item? + * @return mixed + */ + final public function getPrompt() + { + return $this->prompt; + } + + + + /** + * Are the keys used? + * @return bool + */ + final public function areKeysUsed() + { + return $this->useKeys; + } + + + + /** + * Sets items from which to choose. + * @param array + * @param bool + * @return SelectBox provides a fluent interface + */ + public function setItems(array $items, $useKeys = TRUE) + { + $allowed = array(); + foreach ($items as $k => $v) { + foreach ((is_array($v) ? $v : array($k => $v)) as $key => $value) { + if (!$useKeys) { + if (!is_scalar($value)) { + throw new Nette\InvalidArgumentException("All items must be scalar."); + } + $key = $value; + } + + if (isset($allowed[$key])) { + throw new Nette\InvalidArgumentException("Items contain duplication for key '$key'."); + } + + $allowed[$key] = $value; + } + } + + $this->items = $items; + $this->allowed = $allowed; + $this->useKeys = (bool) $useKeys; + return $this; + } + + + + /** + * Returns items from which to choose. + * @return array + */ + final public function getItems() + { + return $this->items; + } + + + + /** + * Returns selected value. + * @return string + */ + public function getSelectedItem() + { + $value = $this->getValue(); + return ($this->useKeys && $value !== NULL) ? $this->allowed[$value] : $value; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $selected = $this->getValue(); + $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE); + $control = parent::getControl(); + $option = Nette\Utils\Html::el('option'); + + if ($this->prompt !== FALSE) { + $control->add($this->prompt instanceof Nette\Utils\Html + ? $this->prompt->value('') + : (string) $option->value('')->setText($this->translate((string) $this->prompt)) + ); + } + + foreach ($this->items as $key => $value) { + if (!is_array($value)) { + $value = array($key => $value); + $dest = $control; + } else { + $dest = $control->create('optgroup')->label($this->translate($key)); + } + + foreach ($value as $key2 => $value2) { + if ($value2 instanceof Nette\Utils\Html) { + $dest->add((string) $value2->selected(isset($selected[$key2]))); + + } else { + $key2 = $this->useKeys ? $key2 : $value2; + $value2 = $this->translate((string) $value2); + $dest->add((string) $option->value($key2) + ->selected(isset($selected[$key2])) + ->setText($value2)); + } + } + } + return $control; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/SubmitButton.php b/apigen/libs/Nette/Nette/Forms/Controls/SubmitButton.php new file mode 100644 index 00000000000..8b36d17694c --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/SubmitButton.php @@ -0,0 +1,123 @@ +control->type = 'submit'; + } + + + + /** + * Sets 'pressed' indicator. + * @param bool + * @return SubmitButton provides a fluent interface + */ + public function setValue($value) + { + if ($this->value = $value !== NULL) { + $this->getForm()->setSubmittedBy($this); + } + return $this; + } + + + + /** + * Tells if the form was submitted by this button. + * @return bool + */ + public function isSubmittedBy() + { + return $this->getForm()->isSubmitted() === $this; + } + + + + /** + * Sets the validation scope. Clicking the button validates only the controls within the specified scope. + * @param mixed + * @return SubmitButton provides a fluent interface + */ + public function setValidationScope($scope) + { + // TODO: implement groups + $this->validationScope = (bool) $scope; + $this->control->formnovalidate = !$this->validationScope; + return $this; + } + + + + /** + * Gets the validation scope. + * @return mixed + */ + final public function getValidationScope() + { + return $this->validationScope; + } + + + + /** + * Fires click event. + * @return void + */ + public function click() + { + $this->onClick($this); + } + + + + /** + * Submitted validator: has been button pressed? + * @param Nette\Forms\ISubmitterControl + * @return bool + */ + public static function validateSubmitted(Nette\Forms\ISubmitterControl $control) + { + return $control->isSubmittedBy(); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/TextArea.php b/apigen/libs/Nette/Nette/Forms/Controls/TextArea.php new file mode 100644 index 00000000000..b0c68b1850d --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/TextArea.php @@ -0,0 +1,53 @@ +control->setName('textarea'); + $this->control->cols = $cols; + $this->control->rows = $rows; + $this->value = ''; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $control = parent::getControl(); + $control->setText($this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value); + return $control; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/TextBase.php b/apigen/libs/Nette/Nette/Forms/Controls/TextBase.php new file mode 100644 index 00000000000..0bfee49515d --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/TextBase.php @@ -0,0 +1,265 @@ +value = is_array($value) ? '' : (string) $value; + return $this; + } + + + + /** + * Returns control's value. + * @return string + */ + public function getValue() + { + $value = $this->value; + foreach ($this->filters as $filter) { + $value = (string) $filter/*5.2*->invoke*/($value); + } + return $value === $this->translate($this->emptyValue) ? '' : $value; + } + + + + /** + * Sets the special value which is treated as empty string. + * @param string + * @return TextBase provides a fluent interface + */ + public function setEmptyValue($value) + { + $this->emptyValue = (string) $value; + return $this; + } + + + + /** + * Returns the special value which is treated as empty string. + * @return string + */ + final public function getEmptyValue() + { + return $this->emptyValue; + } + + + + /** + * Appends input string filter callback. + * @param callable + * @return TextBase provides a fluent interface + */ + public function addFilter($filter) + { + $this->filters[] = new Nette\Callback($filter); + return $this; + } + + + + public function getControl() + { + $control = parent::getControl(); + foreach ($this->getRules() as $rule) { + if ($rule->type === Nette\Forms\Rule::VALIDATOR && !$rule->isNegative + && ($rule->operation === Form::LENGTH || $rule->operation === Form::MAX_LENGTH) + ) { + $control->maxlength = is_array($rule->arg) ? $rule->arg[1] : $rule->arg; + } + } + if ($this->emptyValue !== '') { + $control->data('nette-empty-value', $this->translate($this->emptyValue)); + } + return $control; + } + + + + public function addRule($operation, $message = NULL, $arg = NULL) + { + if ($operation === Form::FLOAT) { + $this->addFilter(array(__CLASS__, 'filterFloat')); + } + return parent::addRule($operation, $message, $arg); + } + + + + /** + * Min-length validator: has control's value minimal length? + * @param TextBase + * @param int length + * @return bool + */ + public static function validateMinLength(TextBase $control, $length) + { + return Strings::length($control->getValue()) >= $length; + } + + + + /** + * Max-length validator: is control's value length in limit? + * @param TextBase + * @param int length + * @return bool + */ + public static function validateMaxLength(TextBase $control, $length) + { + return Strings::length($control->getValue()) <= $length; + } + + + + /** + * Length validator: is control's value length in range? + * @param TextBase + * @param array min and max length pair + * @return bool + */ + public static function validateLength(TextBase $control, $range) + { + if (!is_array($range)) { + $range = array($range, $range); + } + return Validators::isInRange(Strings::length($control->getValue()), $range); + } + + + + /** + * Email validator: is control's value valid email address? + * @param TextBase + * @return bool + */ + public static function validateEmail(TextBase $control) + { + return Validators::isEmail($control->getValue()); + } + + + + /** + * URL validator: is control's value valid URL? + * @param TextBase + * @return bool + */ + public static function validateUrl(TextBase $control) + { + return Validators::isUrl($control->getValue()) || Validators::isUrl('http://' . $control->getValue()); + } + + + + /** @deprecated */ + public static function validateRegexp(TextBase $control, $regexp) + { + return (bool) Strings::match($control->getValue(), $regexp); + } + + + + /** + * Regular expression validator: matches control's value regular expression? + * @param TextBase + * @param string + * @return bool + */ + public static function validatePattern(TextBase $control, $pattern) + { + return (bool) Strings::match($control->getValue(), "\x01^($pattern)$\x01u"); + } + + + + /** + * Integer validator: is a control's value decimal number? + * @param TextBase + * @return bool + */ + public static function validateInteger(TextBase $control) + { + return Validators::isNumericInt($control->getValue()); + } + + + + /** + * Float validator: is a control's value float number? + * @param TextBase + * @return bool + */ + public static function validateFloat(TextBase $control) + { + return Validators::isNumeric(static::filterFloat($control->getValue())); + } + + + + /** + * Rangle validator: is a control's value number in specified range? + * @param TextBase + * @param array min and max value pair + * @return bool + */ + public static function validateRange(TextBase $control, $range) + { + return Validators::isInRange($control->getValue(), $range); + } + + + + /** + * Float string cleanup. + * @param string + * @return string + */ + public static function filterFloat($s) + { + return str_replace(array(' ', ','), array('', '.'), $s); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/TextInput.php b/apigen/libs/Nette/Nette/Forms/Controls/TextInput.php new file mode 100644 index 00000000000..46b672eb376 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/TextInput.php @@ -0,0 +1,104 @@ +control->type = 'text'; + $this->control->size = $cols; + $this->control->maxlength = $maxLength; + $this->addFilter($this->sanitize); + $this->value = ''; + } + + + + /** + * Filter: removes unnecessary whitespace and shortens value to control's max length. + * @return string + */ + public function sanitize($value) + { + if ($this->control->maxlength && Nette\Utils\Strings::length($value) > $this->control->maxlength) { + $value = Nette\Utils\Strings::substring($value, 0, $this->control->maxlength); + } + return Nette\Utils\Strings::trim(strtr($value, "\r\n", ' ')); + } + + + + /** + * Changes control's type attribute. + * @param string + * @return BaseControl provides a fluent interface + */ + public function setType($type) + { + $this->control->type = $type; + return $this; + } + + + + /** @deprecated */ + public function setPasswordMode($mode = TRUE) + { + $this->control->type = $mode ? 'password' : 'text'; + return $this; + } + + + + /** + * Generates control's HTML element. + * @return Nette\Utils\Html + */ + public function getControl() + { + $control = parent::getControl(); + foreach ($this->getRules() as $rule) { + if ($rule->isNegative || $rule->type !== Nette\Forms\Rule::VALIDATOR) { + + } elseif ($rule->operation === Nette\Forms\Form::RANGE && $control->type !== 'text') { + list($control->min, $control->max) = $rule->arg; + + } elseif ($rule->operation === Nette\Forms\Form::PATTERN) { + $control->pattern = $rule->arg; + } + } + if ($control->type !== 'password') { + $control->value = $this->getValue() === '' ? $this->translate($this->emptyValue) : $this->value; + } + return $control; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Controls/UploadControl.php b/apigen/libs/Nette/Nette/Forms/Controls/UploadControl.php new file mode 100644 index 00000000000..165a55327c7 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Controls/UploadControl.php @@ -0,0 +1,138 @@ +control->type = 'file'; + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param Nette\Forms\IComponent + * @return void + */ + protected function attached($form) + { + if ($form instanceof Nette\Forms\Form) { + if ($form->getMethod() !== Nette\Forms\Form::POST) { + throw new Nette\InvalidStateException('File upload requires method POST.'); + } + $form->getElementPrototype()->enctype = 'multipart/form-data'; + } + parent::attached($form); + } + + + + /** + * Sets control's value. + * @param array|Nette\Http\FileUpload + * @return Nette\Http\FileUpload provides a fluent interface + */ + public function setValue($value) + { + if (is_array($value)) { + $this->value = new Http\FileUpload($value); + + } elseif ($value instanceof Http\FileUpload) { + $this->value = $value; + + } else { + $this->value = new Http\FileUpload(NULL); + } + return $this; + } + + + + /** + * Has been any file uploaded? + * @return bool + */ + public function isFilled() + { + return $this->value instanceof Http\FileUpload && $this->value->isOK(); + } + + + + /** + * FileSize validator: is file size in limit? + * @param UploadControl + * @param int file size limit + * @return bool + */ + public static function validateFileSize(UploadControl $control, $limit) + { + $file = $control->getValue(); + return $file instanceof Http\FileUpload && $file->getSize() <= $limit; + } + + + + /** + * MimeType validator: has file specified mime type? + * @param UploadControl + * @param array|string mime type + * @return bool + */ + public static function validateMimeType(UploadControl $control, $mimeType) + { + $file = $control->getValue(); + if ($file instanceof Http\FileUpload) { + $type = strtolower($file->getContentType()); + $mimeTypes = is_array($mimeType) ? $mimeType : explode(',', $mimeType); + if (in_array($type, $mimeTypes, TRUE)) { + return TRUE; + } + if (in_array(preg_replace('#/.*#', '/*', $type), $mimeTypes, TRUE)) { + return TRUE; + } + } + return FALSE; + } + + + + /** + * Image validator: is file image? + * @param UploadControl + * @return bool + */ + public static function validateImage(UploadControl $control) + { + $file = $control->getValue(); + return $file instanceof Http\FileUpload && $file->isImage(); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Form.php b/apigen/libs/Nette/Nette/Forms/Form.php new file mode 100644 index 00000000000..47f0c1832b0 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Form.php @@ -0,0 +1,644 @@ + element */ + private $element; + + /** @var IFormRenderer */ + private $renderer; + + /** @var Nette\Localization\ITranslator */ + private $translator; + + /** @var ControlGroup[] */ + private $groups = array(); + + /** @var array */ + private $errors = array(); + + + + /** + * Form constructor. + * @param string + */ + public function __construct($name = NULL) + { + $this->element = Nette\Utils\Html::el('form'); + $this->element->action = ''; // RFC 1808 -> empty uri means 'this' + $this->element->method = self::POST; + $this->element->id = $name === NULL ? NULL : 'frm-' . $name; + + $this->monitor(__CLASS__); + if ($name !== NULL) { + $tracker = new Controls\HiddenField($name); + $tracker->unmonitor(__CLASS__); + $this[self::TRACKER_ID] = $tracker; + } + parent::__construct(NULL, $name); + } + + + + /** + * This method will be called when the component (or component's parent) + * becomes attached to a monitored object. Do not call this method yourself. + * @param IComponent + * @return void + */ + protected function attached($obj) + { + if ($obj instanceof self) { + throw new Nette\InvalidStateException('Nested forms are forbidden.'); + } + } + + + + /** + * Returns self. + * @return Form + */ + final public function getForm($need = TRUE) + { + return $this; + } + + + + /** + * Sets form's action. + * @param mixed URI + * @return Form provides a fluent interface + */ + public function setAction($url) + { + $this->element->action = $url; + return $this; + } + + + + /** + * Returns form's action. + * @return mixed URI + */ + public function getAction() + { + return $this->element->action; + } + + + + /** + * Sets form's method. + * @param string get | post + * @return Form provides a fluent interface + */ + public function setMethod($method) + { + if ($this->httpData !== NULL) { + throw new Nette\InvalidStateException(__METHOD__ . '() must be called until the form is empty.'); + } + $this->element->method = strtolower($method); + return $this; + } + + + + /** + * Returns form's method. + * @return string get | post + */ + public function getMethod() + { + return $this->element->method; + } + + + + /** + * Cross-Site Request Forgery (CSRF) form protection. + * @param string + * @param int + * @return void + */ + public function addProtection($message = NULL, $timeout = NULL) + { + $session = $this->getSession()->getSection('Nette.Forms.Form/CSRF'); + $key = "key$timeout"; + if (isset($session->$key)) { + $token = $session->$key; + } else { + $session->$key = $token = Nette\Utils\Strings::random(); + } + $session->setExpiration($timeout, $key); + $this[self::PROTECTOR_ID] = new Controls\HiddenField($token); + $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token); + } + + + + /** + * Adds fieldset group to the form. + * @param string caption + * @param bool set this group as current + * @return ControlGroup + */ + public function addGroup($caption = NULL, $setAsCurrent = TRUE) + { + $group = new ControlGroup; + $group->setOption('label', $caption); + $group->setOption('visual', TRUE); + + if ($setAsCurrent) { + $this->setCurrentGroup($group); + } + + if (isset($this->groups[$caption])) { + return $this->groups[] = $group; + } else { + return $this->groups[$caption] = $group; + } + } + + + + /** + * Removes fieldset group from form. + * @param string|FormGroup + * @return void + */ + public function removeGroup($name) + { + if (is_string($name) && isset($this->groups[$name])) { + $group = $this->groups[$name]; + + } elseif ($name instanceof ControlGroup && in_array($name, $this->groups, TRUE)) { + $group = $name; + $name = array_search($group, $this->groups, TRUE); + + } else { + throw new Nette\InvalidArgumentException("Group not found in form '$this->name'"); + } + + foreach ($group->getControls() as $control) { + $this->removeComponent($control); + } + + unset($this->groups[$name]); + } + + + + /** + * Returns all defined groups. + * @return FormGroup[] + */ + public function getGroups() + { + return $this->groups; + } + + + + /** + * Returns the specified group. + * @param string name + * @return ControlGroup + */ + public function getGroup($name) + { + return isset($this->groups[$name]) ? $this->groups[$name] : NULL; + } + + + + /********************* translator ****************d*g**/ + + + + /** + * Sets translate adapter. + * @param Nette\Localization\ITranslator + * @return Form provides a fluent interface + */ + public function setTranslator(Nette\Localization\ITranslator $translator = NULL) + { + $this->translator = $translator; + return $this; + } + + + + /** + * Returns translate adapter. + * @return Nette\Localization\ITranslator|NULL + */ + final public function getTranslator() + { + return $this->translator; + } + + + + /********************* submission ****************d*g**/ + + + + /** + * Tells if the form is anchored. + * @return bool + */ + public function isAnchored() + { + return TRUE; + } + + + + /** + * Tells if the form was submitted. + * @return ISubmitterControl|FALSE submittor control + */ + final public function isSubmitted() + { + if ($this->submittedBy === NULL && count($this->getControls())) { + $this->getHttpData(); + $this->submittedBy = $this->httpData !== NULL; + } + return $this->submittedBy; + } + + + + /** + * Tells if the form was submitted and successfully validated. + * @return bool + */ + final public function isSuccess() + { + return $this->isSubmitted() && $this->isValid(); + } + + + + /** + * Sets the submittor control. + * @param ISubmitterControl + * @return Form provides a fluent interface + */ + public function setSubmittedBy(ISubmitterControl $by = NULL) + { + $this->submittedBy = $by === NULL ? FALSE : $by; + return $this; + } + + + + /** + * Returns submitted HTTP data. + * @return array + */ + final public function getHttpData() + { + if ($this->httpData === NULL) { + if (!$this->isAnchored()) { + throw new Nette\InvalidStateException('Form is not anchored and therefore can not determine whether it was submitted.'); + } + $this->httpData = $this->receiveHttpData(); + } + return $this->httpData; + } + + + + /** + * Fires submit/click events. + * @return void + */ + public function fireEvents() + { + if (!$this->isSubmitted()) { + return; + + } elseif ($this->submittedBy instanceof ISubmitterControl) { + if (!$this->submittedBy->getValidationScope() || $this->isValid()) { + $this->submittedBy->click(); + $valid = TRUE; + } else { + $this->submittedBy->onInvalidClick($this->submittedBy); + } + } + + if (isset($valid) || $this->isValid()) { + $this->onSuccess($this); + } else { + $this->onError($this); + if ($this->onInvalidSubmit) { + trigger_error(__CLASS__ . '->onInvalidSubmit is deprecated; use onError instead.', E_USER_WARNING); + $this->onInvalidSubmit($this); + } + } + + if ($this->onSuccess) { // back compatibility + $this->onSubmit($this); + } elseif ($this->onSubmit) { + trigger_error(__CLASS__ . '->onSubmit changed its behavior; use onSuccess instead.', E_USER_WARNING); + if (isset($valid) || $this->isValid()) { + $this->onSubmit($this); + } + } + } + + + + /** + * Internal: receives submitted HTTP data. + * @return array + */ + protected function receiveHttpData() + { + $httpRequest = $this->getHttpRequest(); + if (strcasecmp($this->getMethod(), $httpRequest->getMethod())) { + return; + } + + if ($httpRequest->isMethod('post')) { + $data = Nette\Utils\Arrays::mergeTree($httpRequest->getPost(), $httpRequest->getFiles()); + } else { + $data = $httpRequest->getQuery(); + } + + if ($tracker = $this->getComponent(self::TRACKER_ID, FALSE)) { + if (!isset($data[self::TRACKER_ID]) || $data[self::TRACKER_ID] !== $tracker->getValue()) { + return; + } + } + + return $data; + } + + + + /********************* data exchange ****************d*g**/ + + + + /** + * Returns the values submitted by the form. + * @return Nette\ArrayHash|array + */ + public function getValues($asArray = FALSE) + { + $values = parent::getValues($asArray); + unset($values[self::TRACKER_ID], $values[self::PROTECTOR_ID]); + return $values; + } + + + + /********************* validation ****************d*g**/ + + + + /** + * Adds error message to the list. + * @param string error message + * @return void + */ + public function addError($message) + { + $this->valid = FALSE; + if ($message !== NULL && !in_array($message, $this->errors, TRUE)) { + $this->errors[] = $message; + } + } + + + + /** + * Returns validation errors. + * @return array + */ + public function getErrors() + { + return $this->errors; + } + + + + /** + * @return bool + */ + public function hasErrors() + { + return (bool) $this->getErrors(); + } + + + + /** + * @return void + */ + public function cleanErrors() + { + $this->errors = array(); + $this->valid = NULL; + } + + + + /********************* rendering ****************d*g**/ + + + + /** + * Returns form's HTML element template. + * @return Nette\Utils\Html + */ + public function getElementPrototype() + { + return $this->element; + } + + + + /** + * Sets form renderer. + * @param IFormRenderer + * @return Form provides a fluent interface + */ + public function setRenderer(IFormRenderer $renderer) + { + $this->renderer = $renderer; + return $this; + } + + + + /** + * Returns form renderer. + * @return IFormRenderer + */ + final public function getRenderer() + { + if ($this->renderer === NULL) { + $this->renderer = new Rendering\DefaultFormRenderer; + } + return $this->renderer; + } + + + + /** + * Renders form. + * @return void + */ + public function render() + { + $args = func_get_args(); + array_unshift($args, $this); + echo call_user_func_array(array($this->getRenderer(), 'render'), $args); + } + + + + /** + * Renders form to string. + * @return bool can throw exceptions? (hidden parameter) + * @return string + */ + public function __toString() + { + try { + return $this->getRenderer()->render($this); + + } catch (\Exception $e) { + if (func_get_args() && func_get_arg(0)) { + throw $e; + } else { + Nette\Diagnostics\Debugger::toStringException($e); + } + } + } + + + + /********************* backend ****************d*g**/ + + + + /** + * @return Nette\Http\IRequest + */ + protected function getHttpRequest() + { + return Nette\Environment::getHttpRequest(); + } + + + + /** + * @return Nette\Http\Session + */ + protected function getSession() + { + return Nette\Environment::getSession(); + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/IControl.php b/apigen/libs/Nette/Nette/Forms/IControl.php new file mode 100644 index 00000000000..d6845f923f1 --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/IControl.php @@ -0,0 +1,70 @@ + array( + 'container' => NULL, + 'errors' => TRUE, + ), + + 'error' => array( + 'container' => 'ul class=error', + 'item' => 'li', + ), + + 'group' => array( + 'container' => 'fieldset', + 'label' => 'legend', + 'description' => 'p', + ), + + 'controls' => array( + 'container' => 'table', + ), + + 'pair' => array( + 'container' => 'tr', + '.required' => 'required', + '.optional' => NULL, + '.odd' => NULL, + ), + + 'control' => array( + 'container' => 'td', + '.odd' => NULL, + + 'errors' => FALSE, + 'description' => 'small', + 'requiredsuffix' => '', + + '.required' => 'required', + '.text' => 'text', + '.password' => 'text', + '.file' => 'text', + '.submit' => 'button', + '.image' => 'imagebutton', + '.button' => 'button', + ), + + 'label' => array( + 'container' => 'th', + 'suffix' => NULL, + 'requiredsuffix' => '', + ), + + 'hidden' => array( + 'container' => 'div', + ), + ); + + /** @var Nette\Forms\Form */ + protected $form; + + /** @var int */ + protected $counter; + + + + /** + * Provides complete form rendering. + * @param Nette\Forms\Form + * @param string 'begin', 'errors', 'body', 'end' or empty to render all + * @return string + */ + public function render(Nette\Forms\Form $form, $mode = NULL) + { + if ($this->form !== $form) { + $this->form = $form; + $this->init(); + } + + $s = ''; + if (!$mode || $mode === 'begin') { + $s .= $this->renderBegin(); + } + if ((!$mode && $this->getValue('form errors')) || $mode === 'errors') { + $s .= $this->renderErrors(); + } + if (!$mode || $mode === 'body') { + $s .= $this->renderBody(); + } + if (!$mode || $mode === 'end') { + $s .= $this->renderEnd(); + } + return $s; + } + + + + /** @deprecated */ + public function setClientScript() + { + trigger_error(__METHOD__ . '() is deprecated; use unobstructive JavaScript instead.', E_USER_WARNING); + return $this; + } + + + + /** + * Initializes form. + * @return void + */ + protected function init() + { + // TODO: only for back compatiblity - remove? + $wrapper = & $this->wrappers['control']; + foreach ($this->form->getControls() as $control) { + if ($control->isRequired() && isset($wrapper['.required'])) { + $control->getLabelPrototype()->class($wrapper['.required'], TRUE); + } + + $el = $control->getControlPrototype(); + if ($el->getName() === 'input' && isset($wrapper['.' . $el->type])) { + $el->class($wrapper['.' . $el->type], TRUE); + } + } + } + + + + /** + * Renders form begin. + * @return string + */ + public function renderBegin() + { + $this->counter = 0; + + foreach ($this->form->getControls() as $control) { + $control->setOption('rendered', FALSE); + } + + if (strcasecmp($this->form->getMethod(), 'get') === 0) { + $el = clone $this->form->getElementPrototype(); + $url = explode('?', (string) $el->action, 2); + $el->action = $url[0]; + $s = ''; + if (isset($url[1])) { + foreach (preg_split('#[;&]#', $url[1]) as $param) { + $parts = explode('=', $param, 2); + $name = urldecode($parts[0]); + if (!isset($this->form[$name])) { + $s .= Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1]))); + } + } + $s = "\n\t" . $this->getWrapper('hidden container')->setHtml($s); + } + return $el->startTag() . $s; + + + } else { + return $this->form->getElementPrototype()->startTag(); + } + } + + + + /** + * Renders form end. + * @return string + */ + public function renderEnd() + { + $s = ''; + foreach ($this->form->getControls() as $control) { + if ($control instanceof Nette\Forms\Controls\HiddenField && !$control->getOption('rendered')) { + $s .= (string) $control->getControl(); + } + } + if (iterator_count($this->form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) { + $s .= ''; + } + if ($s) { + $s = $this->getWrapper('hidden container')->setHtml($s) . "\n"; + } + + return $s . $this->form->getElementPrototype()->endTag() . "\n"; + } + + + + /** + * Renders validation errors (per form or per control). + * @param Nette\Forms\IControl + * @return string + */ + public function renderErrors(Nette\Forms\IControl $control = NULL) + { + $errors = $control === NULL ? $this->form->getErrors() : $control->getErrors(); + if (count($errors)) { + $ul = $this->getWrapper('error container'); + $li = $this->getWrapper('error item'); + + foreach ($errors as $error) { + $item = clone $li; + if ($error instanceof Html) { + $item->add($error); + } else { + $item->setText($error); + } + $ul->add($item); + } + return "\n" . $ul->render(0); + } + } + + + + /** + * Renders form body. + * @return string + */ + public function renderBody() + { + $s = $remains = ''; + + $defaultContainer = $this->getWrapper('group container'); + $translator = $this->form->getTranslator(); + + foreach ($this->form->getGroups() as $group) { + if (!$group->getControls() || !$group->getOption('visual')) { + continue; + } + + $container = $group->getOption('container', $defaultContainer); + $container = $container instanceof Html ? clone $container : Html::el($container); + + $s .= "\n" . $container->startTag(); + + $text = $group->getOption('label'); + if ($text instanceof Html) { + $s .= $text; + + } elseif (is_string($text)) { + if ($translator !== NULL) { + $text = $translator->translate($text); + } + $s .= "\n" . $this->getWrapper('group label')->setText($text) . "\n"; + } + + $text = $group->getOption('description'); + if ($text instanceof Html) { + $s .= $text; + + } elseif (is_string($text)) { + if ($translator !== NULL) { + $text = $translator->translate($text); + } + $s .= $this->getWrapper('group description')->setText($text) . "\n"; + } + + $s .= $this->renderControls($group); + + $remains = $container->endTag() . "\n" . $remains; + if (!$group->getOption('embedNext')) { + $s .= $remains; + $remains = ''; + } + } + + $s .= $remains . $this->renderControls($this->form); + + $container = $this->getWrapper('form container'); + $container->setHtml($s); + return $container->render(0); + } + + + + /** + * Renders group of controls. + * @param Nette\Forms\Container|FormGroup + * @return string + */ + public function renderControls($parent) + { + if (!($parent instanceof Nette\Forms\Container || $parent instanceof Nette\Forms\ControlGroup)) { + throw new Nette\InvalidArgumentException("Argument must be FormContainer or FormGroup instance."); + } + + $container = $this->getWrapper('controls container'); + + $buttons = NULL; + foreach ($parent->getControls() as $control) { + if ($control->getOption('rendered') || $control instanceof Nette\Forms\Controls\HiddenField || $control->getForm(FALSE) !== $this->form) { + // skip + + } elseif ($control instanceof Nette\Forms\Controls\Button) { + $buttons[] = $control; + + } else { + if ($buttons) { + $container->add($this->renderPairMulti($buttons)); + $buttons = NULL; + } + $container->add($this->renderPair($control)); + } + } + + if ($buttons) { + $container->add($this->renderPairMulti($buttons)); + } + + $s = ''; + if (count($container)) { + $s .= "\n" . $container . "\n"; + } + + return $s; + } + + + + /** + * Renders single visual row. + * @param Nette\Forms\IControl + * @return string + */ + public function renderPair(Nette\Forms\IControl $control) + { + $pair = $this->getWrapper('pair container'); + $pair->add($this->renderLabel($control)); + $pair->add($this->renderControl($control)); + $pair->class($this->getValue($control->isRequired() ? 'pair .required' : 'pair .optional'), TRUE); + $pair->class($control->getOption('class'), TRUE); + if (++$this->counter % 2) { + $pair->class($this->getValue('pair .odd'), TRUE); + } + $pair->id = $control->getOption('id'); + return $pair->render(0); + } + + + + /** + * Renders single visual row of multiple controls. + * @param IFormControl[] + * @return string + */ + public function renderPairMulti(array $controls) + { + $s = array(); + foreach ($controls as $control) { + if (!$control instanceof Nette\Forms\IControl) { + throw new Nette\InvalidArgumentException("Argument must be array of IFormControl instances."); + } + $s[] = (string) $control->getControl(); + } + $pair = $this->getWrapper('pair container'); + $pair->add($this->renderLabel($control)); + $pair->add($this->getWrapper('control container')->setHtml(implode(" ", $s))); + return $pair->render(0); + } + + + + /** + * Renders 'label' part of visual row of controls. + * @param Nette\Forms\IControl + * @return string + */ + public function renderLabel(Nette\Forms\IControl $control) + { + $head = $this->getWrapper('label container'); + + if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { + return $head->setHtml(($head->getName() === 'td' || $head->getName() === 'th') ? ' ' : ''); + + } else { + $label = $control->getLabel(); + $suffix = $this->getValue('label suffix') . ($control->isRequired() ? $this->getValue('label requiredsuffix') : ''); + if ($label instanceof Html) { + $label->setHtml($label->getHtml() . $suffix); + $suffix = ''; + } + return $head->setHtml((string) $label . $suffix); + } + } + + + + /** + * Renders 'control' part of visual row of controls. + * @param Nette\Forms\IControl + * @return string + */ + public function renderControl(Nette\Forms\IControl $control) + { + $body = $this->getWrapper('control container'); + if ($this->counter % 2) { + $body->class($this->getValue('control .odd'), TRUE); + } + + $description = $control->getOption('description'); + if ($description instanceof Html) { + $description = ' ' . $control->getOption('description'); + + } elseif (is_string($description)) { + $description = ' ' . $this->getWrapper('control description')->setText($control->translate($description)); + + } else { + $description = ''; + } + + if ($control->isRequired()) { + $description = $this->getValue('control requiredsuffix') . $description; + } + + if ($this->getValue('control errors')) { + $description .= $this->renderErrors($control); + } + + if ($control instanceof Nette\Forms\Controls\Checkbox || $control instanceof Nette\Forms\Controls\Button) { + return $body->setHtml((string) $control->getControl() . (string) $control->getLabel() . $description); + + } else { + return $body->setHtml((string) $control->getControl() . $description); + } + } + + + + /** + * @param string + * @return Nette\Utils\Html + */ + protected function getWrapper($name) + { + $data = $this->getValue($name); + return $data instanceof Html ? clone $data : Html::el($data); + } + + + + /** + * @param string + * @return string + */ + protected function getValue($name) + { + $name = explode(' ', $name); + $data = & $this->wrappers[$name[0]][$name[1]]; + return $data; + } + +} diff --git a/apigen/libs/Nette/Nette/Forms/Rule.php b/apigen/libs/Nette/Nette/Forms/Rule.php new file mode 100644 index 00000000000..60ee09e8e1a --- /dev/null +++ b/apigen/libs/Nette/Nette/Forms/Rule.php @@ -0,0 +1,55 @@ + 'Security token did not match. Possible CSRF attack.', + Form::EQUAL => 'Please enter %s.', + Form::FILLED => 'Please complete mandatory field.', + Form::MIN_LENGTH => 'Please enter a value of at least %d characters.', + Form::MAX_LENGTH => 'Please enter a value no longer than %d characters.', + Form::LENGTH => 'Please enter a value between %d and %d characters long.', + Form::EMAIL => 'Please enter a valid email address.', + Form::URL => 'Please enter a valid URL.', + Form::INTEGER => 'Please enter a numeric value.', + Form::FLOAT => 'Please enter a numeric value.', + Form::RANGE => 'Please enter a value between %d and %d.', + Form::MAX_FILE_SIZE => 'The size of the uploaded file can be up to %d bytes.', + Form::IMAGE => 'The uploaded file must be image in format JPEG, GIF or PNG.', + ); + + /** @var Rule[] */ + private $rules = array(); + + /** @var Rules */ + private $parent; + + /** @var array */ + private $toggles = array(); + + /** @var IControl */ + private $control; + + + + public function __construct(IControl $control) + { + $this->control = $control; + } + + + + /** + * Adds a validation rule for the current control. + * @param mixed rule type + * @param string message to display for invalid data + * @param mixed optional rule arguments + * @return Rules provides a fluent interface + */ + public function addRule($operation, $message = NULL, $arg = NULL) + { + $rule = new Rule; + $rule->control = $this->control; + $rule->operation = $operation; + $this->adjustOperation($rule); + $rule->arg = $arg; + $rule->type = Rule::VALIDATOR; + if ($message === NULL && is_string($rule->operation) && isset(static::$defaultMessages[$rule->operation])) { + $rule->message = static::$defaultMessages[$rule->operation]; + } else { + $rule->message = $message; + } + $this->rules[] = $rule; + return $this; + } + + + + /** + * Adds a validation condition a returns new branch. + * @param mixed condition type + * @param mixed optional condition arguments + * @return Rules new branch + */ + public function addCondition($operation, $arg = NULL) + { + return $this->addConditionOn($this->control, $operation, $arg); + } + + + + /** + * Adds a validation condition on specified control a returns new branch. + * @param IControl form control + * @param mixed condition type + * @param mixed optional condition arguments + * @return Rules new branch + */ + public function addConditionOn(IControl $control, $operation, $arg = NULL) + { + $rule = new Rule; + $rule->control = $control; + $rule->operation = $operation; + $this->adjustOperation($rule); + $rule->arg = $arg; + $rule->type = Rule::CONDITION; + $rule->subRules = new static($this->control); + $rule->subRules->parent = $this; + + $this->rules[] = $rule; + return $rule->subRules; + } + + + + /** + * Adds a else statement. + * @return Rules else branch + */ + public function elseCondition() + { + $rule = clone end($this->parent->rules); + $rule->isNegative = !$rule->isNegative; + $rule->subRules = new static($this->parent->control); + $rule->subRules->parent = $this->parent; + $this->parent->rules[] = $rule; + return $rule->subRules; + } + + + + /** + * Ends current validation condition. + * @return Rules parent branch + */ + public function endCondition() + { + return $this->parent; + } + + + + /** + * Toggles HTML elememnt visibility. + * @param string element id + * @param bool hide element? + * @return Rules provides a fluent interface + */ + public function toggle($id, $hide = TRUE) + { + $this->toggles[$id] = $hide; + return $this; + } + + + + /** + * Validates against ruleset. + * @param bool stop before first error? + * @return bool is valid? + */ + public function validate($onlyCheck = FALSE) + { + foreach ($this->rules as $rule) { + if ($rule->control->isDisabled()) { + continue; + } + + $success = ($rule->isNegative xor $this->getCallback($rule)->invoke($rule->control, $rule->arg)); + + if ($rule->type === Rule::CONDITION && $success) { + if (!$rule->subRules->validate($onlyCheck)) { + return FALSE; + } + + } elseif ($rule->type === Rule::VALIDATOR && !$success) { + if (!$onlyCheck) { + $rule->control->addError(static::formatMessage($rule, TRUE)); + } + return FALSE; + } + } + return TRUE; + } + + + + /** + * Iterates over ruleset. + * @return \ArrayIterator + */ + final public function getIterator() + { + return new \ArrayIterator($this->rules); + } + + + + /** + * @return array + */ + final public function getToggles() + { + return $this->toggles; + } + + + + /** + * Process 'operation' string. + * @param Rule + * @return void + */ + private function adjustOperation($rule) + { + if (is_string($rule->operation) && ord($rule->operation[0]) > 127) { + $rule->isNegative = TRUE; + $rule->operation = ~$rule->operation; + } + + if (!$this->getCallback($rule)->isCallable()) { + $operation = is_scalar($rule->operation) ? " '$rule->operation'" : ''; + throw new Nette\InvalidArgumentException("Unknown operation$operation for control '{$rule->control->name}'."); + } + } + + + + private function getCallback($rule) + { + $op = $rule->operation; + if (is_string($op) && strncmp($op, ':', 1) === 0) { + return new Nette\Callback(get_class($rule->control), self::VALIDATE_PREFIX . ltrim($op, ':')); + } else { + return new Nette\Callback($op); + } + } + + + + public static function formatMessage($rule, $withValue) + { + $message = $rule->message; + if ($message instanceof Nette\Utils\Html) { + return $message; + } + if (!isset($message)) { // report missing message by notice + $message = static::$defaultMessages[$rule->operation]; + } + if ($translator = $rule->control->getForm()->getTranslator()) { + $message = $translator->translate($message, is_int($rule->arg) ? $rule->arg : NULL); + } + $message = vsprintf(preg_replace('#%(name|label|value)#', '%$0', $message), (array) $rule->arg); + $message = str_replace('%name', $rule->control->getName(), $message); + $message = str_replace('%label', $rule->control->translate($rule->control->caption), $message); + if ($withValue && strpos($message, '%value') !== FALSE) { + $message = str_replace('%value', $rule->control->getValue(), $message); + } + return $message; + } + +} diff --git a/apigen/libs/Nette/Nette/Http/Context.php b/apigen/libs/Nette/Nette/Http/Context.php new file mode 100644 index 00000000000..1ea71bce697 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/Context.php @@ -0,0 +1,114 @@ +request = $request; + $this->response = $response; + } + + + + /** + * Attempts to cache the sent entity by its last modification date. + * @param string|int|DateTime last modified time + * @param string strong entity tag validator + * @return bool + */ + public function isModified($lastModified = NULL, $etag = NULL) + { + if ($lastModified) { + $this->response->setHeader('Last-Modified', $this->response->date($lastModified)); + } + if ($etag) { + $this->response->setHeader('ETag', '"' . addslashes($etag) . '"'); + } + + $ifNoneMatch = $this->request->getHeader('If-None-Match'); + if ($ifNoneMatch === '*') { + $match = TRUE; // match, check if-modified-since + + } elseif ($ifNoneMatch !== NULL) { + $etag = $this->response->getHeader('ETag'); + + if ($etag == NULL || strpos(' ' . strtr($ifNoneMatch, ",\t", ' '), ' ' . $etag) === FALSE) { + return TRUE; + + } else { + $match = TRUE; // match, check if-modified-since + } + } + + $ifModifiedSince = $this->request->getHeader('If-Modified-Since'); + if ($ifModifiedSince !== NULL) { + $lastModified = $this->response->getHeader('Last-Modified'); + if ($lastModified != NULL && strtotime($lastModified) <= strtotime($ifModifiedSince)) { + $match = TRUE; + + } else { + return TRUE; + } + } + + if (empty($match)) { + return TRUE; + } + + $this->response->setCode(IResponse::S304_NOT_MODIFIED); + return FALSE; + } + + + + /** + * @return IRequest + */ + public function getRequest() + { + return $this->request; + } + + + + /** + * @return IResponse + */ + public function getResponse() + { + return $this->response; + } + +} diff --git a/apigen/libs/Nette/Nette/Http/FileUpload.php b/apigen/libs/Nette/Nette/Http/FileUpload.php new file mode 100644 index 00000000000..480511fe206 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/FileUpload.php @@ -0,0 +1,222 @@ +error = UPLOAD_ERR_NO_FILE; + return; // or throw exception? + } + } + $this->name = $value['name']; + $this->size = $value['size']; + $this->tmpName = $value['tmp_name']; + $this->error = $value['error']; + } + + + + /** + * Returns the file name. + * @return string + */ + public function getName() + { + return $this->name; + } + + + + /** + * Returns the sanitized file name. + * @return string + */ + public function getSanitizedName() + { + return trim(Nette\Utils\Strings::webalize($this->name, '.', FALSE), '.-'); + } + + + + /** + * Returns the MIME content type of an uploaded file. + * @return string + */ + public function getContentType() + { + if ($this->isOk() && $this->type === NULL) { + $this->type = Nette\Utils\MimeTypeDetector::fromFile($this->tmpName); + } + return $this->type; + } + + + + /** + * Returns the size of an uploaded file. + * @return int + */ + public function getSize() + { + return $this->size; + } + + + + /** + * Returns the path to an uploaded file. + * @return string + */ + public function getTemporaryFile() + { + return $this->tmpName; + } + + + + /** + * Returns the path to an uploaded file. + * @return string + */ + public function __toString() + { + return $this->tmpName; + } + + + + /** + * Returns the error code. {@link http://php.net/manual/en/features.file-upload.errors.php} + * @return int + */ + public function getError() + { + return $this->error; + } + + + + /** + * Is there any error? + * @return bool + */ + public function isOk() + { + return $this->error === UPLOAD_ERR_OK; + } + + + + /** + * Move uploaded file to new location. + * @param string + * @return FileUpload provides a fluent interface + */ + public function move($dest) + { + @mkdir(dirname($dest), 0777, TRUE); // @ - dir may already exist + /*5.2*if (substr(PHP_OS, 0, 3) === 'WIN') { @unlink($dest); }*/ + if (!call_user_func(is_uploaded_file($this->tmpName) ? 'move_uploaded_file' : 'rename', $this->tmpName, $dest)) { + throw new Nette\InvalidStateException("Unable to move uploaded file '$this->tmpName' to '$dest'."); + } + chmod($dest, 0666); + $this->tmpName = $dest; + return $this; + } + + + + /** + * Is uploaded file GIF, PNG or JPEG? + * @return bool + */ + public function isImage() + { + return in_array($this->getContentType(), array('image/gif', 'image/png', 'image/jpeg'), TRUE); + } + + + + /** + * Returns the image. + * @return Nette\Image + */ + public function toImage() + { + return Nette\Image::fromFile($this->tmpName); + } + + + + /** + * Returns the dimensions of an uploaded image as array. + * @return array + */ + public function getImageSize() + { + return $this->isOk() ? @getimagesize($this->tmpName) : NULL; // @ - files smaller than 12 bytes causes read error + } + + + + /** + * Get file contents. + * @return string + */ + public function getContents() + { + // future implementation can try to work around safe_mode and open_basedir limitations + return $this->isOk() ? file_get_contents($this->tmpName) : NULL; + } + +} diff --git a/apigen/libs/Nette/Nette/Http/IRequest.php b/apigen/libs/Nette/Nette/Http/IRequest.php new file mode 100644 index 00000000000..0ccb12316a3 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/IRequest.php @@ -0,0 +1,140 @@ +url = $url; + $this->url->freeze(); + if ($query === NULL) { + parse_str($url->query, $this->query); + } else { + $this->query = (array) $query; + } + $this->post = (array) $post; + $this->files = (array) $files; + $this->cookies = (array) $cookies; + $this->headers = (array) $headers; + $this->method = $method; + $this->remoteAddress = $remoteAddress; + $this->remoteHost = $remoteHost; + } + + + + /** + * Returns URL object. + * @return UrlScript + */ + final public function getUrl() + { + return $this->url; + } + + + + /** @deprecated */ + function getUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getUrl() instead.', E_USER_WARNING); + return $this->getUrl(); + } + + + + /********************* query, post, files & cookies ****************d*g**/ + + + + /** + * Returns variable provided to the script via URL query ($_GET). + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getQuery($key = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->query; + + } elseif (isset($this->query[$key])) { + return $this->query[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns variable provided to the script via POST method ($_POST). + * If no key is passed, returns the entire array. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getPost($key = NULL, $default = NULL) + { + if (func_num_args() === 0) { + return $this->post; + + } elseif (isset($this->post[$key])) { + return $this->post[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns uploaded file. + * @param string key (or more keys) + * @return FileUpload + */ + final public function getFile($key) + { + $args = func_get_args(); + return Nette\Utils\Arrays::get($this->files, $args, NULL); + } + + + + /** + * Returns uploaded files. + * @return array + */ + final public function getFiles() + { + return $this->files; + } + + + + /** + * Returns variable provided to the script via HTTP cookies. + * @param string key + * @param mixed default value + * @return mixed + */ + final public function getCookie($key, $default = NULL) + { + if (func_num_args() === 0) { + return $this->cookies; + + } elseif (isset($this->cookies[$key])) { + return $this->cookies[$key]; + + } else { + return $default; + } + } + + + + /** + * Returns variables provided to the script via HTTP cookies. + * @return array + */ + final public function getCookies() + { + return $this->cookies; + } + + + + /********************* method & headers ****************d*g**/ + + + + /** + * Returns HTTP request method (GET, POST, HEAD, PUT, ...). The method is case-sensitive. + * @return string + */ + public function getMethod() + { + return $this->method; + } + + + + /** + * Checks if the request method is the given one. + * @param string + * @return bool + */ + public function isMethod($method) + { + return strcasecmp($this->method, $method) === 0; + } + + + + /** + * Checks if the request method is POST. + * @return bool + */ + public function isPost() + { + return $this->isMethod('POST'); + } + + + + /** + * Return the value of the HTTP header. Pass the header name as the + * plain, HTTP-specified header name (e.g. 'Accept-Encoding'). + * @param string + * @param mixed + * @return mixed + */ + final public function getHeader($header, $default = NULL) + { + $header = strtolower($header); + if (isset($this->headers[$header])) { + return $this->headers[$header]; + } else { + return $default; + } + } + + + + /** + * Returns all HTTP headers. + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + + + + /** + * Returns referrer. + * @return Url|NULL + */ + final public function getReferer() + { + return isset($this->headers['referer']) ? new Url($this->headers['referer']) : NULL; + } + + + + /** + * Is the request is sent via secure channel (https). + * @return bool + */ + public function isSecured() + { + return $this->url->scheme === 'https'; + } + + + + /** + * Is AJAX request? + * @return bool + */ + public function isAjax() + { + return $this->getHeader('X-Requested-With') === 'XMLHttpRequest'; + } + + + + /** + * Returns the IP address of the remote client. + * @return string + */ + public function getRemoteAddress() + { + return $this->remoteAddress; + } + + + + /** + * Returns the host of the remote client. + * @return string + */ + public function getRemoteHost() + { + if (!$this->remoteHost) { + $this->remoteHost = $this->remoteAddress ? getHostByAddr($this->remoteAddress) : NULL; + } + return $this->remoteHost; + } + + + + /** + * Parse Accept-Language header and returns prefered language. + * @param array Supported languages + * @return string + */ + public function detectLanguage(array $langs) + { + $header = $this->getHeader('Accept-Language'); + if (!$header) { + return NULL; + } + + $s = strtolower($header); // case insensitive + $s = strtr($s, '_', '-'); // cs_CZ means cs-CZ + rsort($langs); // first more specific + preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches); + + if (!$matches[0]) { + return NULL; + } + + $max = 0; + $lang = NULL; + foreach ($matches[1] as $key => $value) { + $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key]; + if ($q > $max) { + $max = $q; $lang = $value; + } + } + + return $lang; + } + +} diff --git a/apigen/libs/Nette/Nette/Http/RequestFactory.php b/apigen/libs/Nette/Nette/Http/RequestFactory.php new file mode 100644 index 00000000000..96d10a88976 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/RequestFactory.php @@ -0,0 +1,252 @@ + array('#/{2,}#' => '/'), // '%20' => '' + 'url' => array(), // '#[.,)]$#' => '' + ); + + /** @var string */ + private $encoding; + + + + /** + * @param string + * @return RequestFactory provides a fluent interface + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + return $this; + } + + + + /** + * Creates current HttpRequest object. + * @return Request + */ + public function createHttpRequest() + { + // DETECTS URI, base path and script path of the request. + $url = new UrlScript; + $url->scheme = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https' : 'http'; + $url->user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : ''; + $url->password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : ''; + + // host & port + if (isset($_SERVER['HTTP_HOST'])) { + $pair = explode(':', $_SERVER['HTTP_HOST']); + + } elseif (isset($_SERVER['SERVER_NAME'])) { + $pair = explode(':', $_SERVER['SERVER_NAME']); + + } else { + $pair = array(''); + } + + $url->host = preg_match('#^[-._a-z0-9]+$#', $pair[0]) ? $pair[0] : ''; + + if (isset($pair[1])) { + $url->port = (int) $pair[1]; + + } elseif (isset($_SERVER['SERVER_PORT'])) { + $url->port = (int) $_SERVER['SERVER_PORT']; + } + + // path & query + if (isset($_SERVER['REQUEST_URI'])) { // Apache, IIS 6.0 + $requestUrl = $_SERVER['REQUEST_URI']; + + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0 (PHP as CGI ?) + $requestUrl = $_SERVER['ORIG_PATH_INFO']; + if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') { + $requestUrl .= '?' . $_SERVER['QUERY_STRING']; + } + } else { + $requestUrl = ''; + } + + $requestUrl = Strings::replace($requestUrl, $this->urlFilters['url']); + $tmp = explode('?', $requestUrl, 2); + $url->path = Strings::replace($tmp[0], $this->urlFilters['path']); + $url->query = isset($tmp[1]) ? $tmp[1] : ''; + + // normalized url + $url->canonicalize(); + $url->path = Strings::fixEncoding($url->path); + + // detect script path + if (isset($_SERVER['SCRIPT_NAME'])) { + $script = $_SERVER['SCRIPT_NAME']; + } elseif (isset($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME']) + && strncmp($_SERVER['DOCUMENT_ROOT'], $_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])) === 0 + ) { + $script = '/' . ltrim(strtr(substr($_SERVER['SCRIPT_FILENAME'], strlen($_SERVER['DOCUMENT_ROOT'])), '\\', '/'), '/'); + } else { + $script = '/'; + } + + $path = strtolower($url->path) . '/'; + $script = strtolower($script) . '/'; + $max = min(strlen($path), strlen($script)); + for ($i = 0; $i < $max; $i++) { + if ($path[$i] !== $script[$i]) { + break; + } elseif ($path[$i] === '/') { + $url->scriptPath = substr($url->path, 0, $i + 1); + } + } + + // GET, POST, COOKIE + $useFilter = (!in_array(ini_get('filter.default'), array('', 'unsafe_raw')) || ini_get('filter.default_flags')); + + parse_str($url->query, $query); + if (!$query) { + $query = $useFilter ? filter_input_array(INPUT_GET, FILTER_UNSAFE_RAW) : (empty($_GET) ? array() : $_GET); + } + $post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST); + $cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE); + + $gpc = (bool) get_magic_quotes_gpc(); + $old = error_reporting(error_reporting() ^ E_NOTICE); + + // remove fucking quotes and check (and optionally convert) encoding + if ($gpc || $this->encoding) { + $utf = strcasecmp($this->encoding, 'UTF-8') === 0; + $list = array(& $query, & $post, & $cookies); + while (list($key, $val) = each($list)) { + foreach ($val as $k => $v) { + unset($list[$key][$k]); + + if ($gpc) { + $k = stripslashes($k); + } + + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + // invalid key -> ignore + + } elseif (is_array($v)) { + $list[$key][$k] = $v; + $list[] = & $list[$key][$k]; + + } else { + if ($gpc && !$useFilter) { + $v = stripSlashes($v); + } + if ($this->encoding) { + if ($utf) { + $v = Strings::fixEncoding($v); + + } else { + if (!Strings::checkEncoding($v)) { + $v = iconv($this->encoding, 'UTF-8//IGNORE', $v); + } + $v = html_entity_decode($v, ENT_QUOTES, 'UTF-8'); + } + $v = preg_replace(self::NONCHARS, '', $v); + } + $list[$key][$k] = $v; + } + } + } + unset($list, $key, $val, $k, $v); + } + + + // FILES and create FileUpload objects + $files = array(); + $list = array(); + if (!empty($_FILES)) { + foreach ($_FILES as $k => $v) { + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + continue; + } + $v['@'] = & $files[$k]; + $list[] = $v; + } + } + + while (list(, $v) = each($list)) { + if (!isset($v['name'])) { + continue; + + } elseif (!is_array($v['name'])) { + if ($gpc) { + $v['name'] = stripSlashes($v['name']); + } + if ($this->encoding) { + $v['name'] = preg_replace(self::NONCHARS, '', Strings::fixEncoding($v['name'])); + } + $v['@'] = new FileUpload($v); + continue; + } + + foreach ($v['name'] as $k => $foo) { + if ($this->encoding && is_string($k) && (preg_match(self::NONCHARS, $k) || preg_last_error())) { + continue; + } + $list[] = array( + 'name' => $v['name'][$k], + 'type' => $v['type'][$k], + 'size' => $v['size'][$k], + 'tmp_name' => $v['tmp_name'][$k], + 'error' => $v['error'][$k], + '@' => & $v['@'][$k], + ); + } + } + + error_reporting($old); + + + // HEADERS + if (function_exists('apache_request_headers')) { + $headers = array_change_key_case(apache_request_headers(), CASE_LOWER); + } else { + $headers = array(); + foreach ($_SERVER as $k => $v) { + if (strncmp($k, 'HTTP_', 5) == 0) { + $k = substr($k, 5); + } elseif (strncmp($k, 'CONTENT_', 8)) { + continue; + } + $headers[ strtr(strtolower($k), '_', '-') ] = $v; + } + } + + return new Request($url, $query, $post, $files, $cookies, $headers, + isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : NULL, + isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL, + isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : NULL + ); + } + +} diff --git a/apigen/libs/Nette/Nette/Http/Response.php b/apigen/libs/Nette/Nette/Http/Response.php new file mode 100644 index 00000000000..0a63d2a67f9 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/Response.php @@ -0,0 +1,339 @@ +1, 201=>1, 202=>1, 203=>1, 204=>1, 205=>1, 206=>1, + 300=>1, 301=>1, 302=>1, 303=>1, 304=>1, 307=>1, + 400=>1, 401=>1, 403=>1, 404=>1, 405=>1, 406=>1, 408=>1, 410=>1, 412=>1, 415=>1, 416=>1, + 500=>1, 501=>1, 503=>1, 505=>1 + ); + + if (!isset($allowed[$code])) { + throw new Nette\InvalidArgumentException("Bad HTTP response '$code'."); + + } elseif (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot set HTTP code after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + + } else { + $this->code = $code; + $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + header($protocol . ' ' . $code, TRUE, $code); + } + return $this; + } + + + + /** + * Returns HTTP response code. + * @return int + */ + public function getCode() + { + return $this->code; + } + + + + /** + * Sends a HTTP header and replaces a previous one. + * @param string header name + * @param string header value + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setHeader($name, $value) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + if ($value === NULL && function_exists('header_remove')) { + header_remove($name); + } else { + header($name . ': ' . $value, TRUE, $this->code); + } + return $this; + } + + + + /** + * Adds HTTP header. + * @param string header name + * @param string header value + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function addHeader($name, $value) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot send header after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + header($name . ': ' . $value, FALSE, $this->code); + return $this; + } + + + + /** + * Sends a Content-type HTTP header. + * @param string mime-type + * @param string charset + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setContentType($type, $charset = NULL) + { + $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); + return $this; + } + + + + /** + * Redirects to a new URL. Note: call exit() after it. + * @param string URL + * @param int HTTP code + * @return void + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function redirect($url, $code = self::S302_FOUND) + { + if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('#^Microsoft-IIS/[1-5]#', $_SERVER['SERVER_SOFTWARE']) + && $this->getHeader('Set-Cookie') !== NULL + ) { + $this->setHeader('Refresh', "0;url=$url"); + return; + } + + $this->setCode($code); + $this->setHeader('Location', $url); + echo "

Redirect

\n\n

Please click here to continue.

"; + } + + + + /** + * Sets the number of seconds before a page cached on a browser expires. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setExpiration($time) + { + if (!$time) { // no cache + $this->setHeader('Cache-Control', 's-maxage=0, max-age=0, must-revalidate'); + $this->setHeader('Expires', 'Mon, 23 Jan 1978 10:00:00 GMT'); + return $this; + } + + $time = Nette\DateTime::from($time); + $this->setHeader('Cache-Control', 'max-age=' . ($time->format('U') - time())); + $this->setHeader('Expires', self::date($time)); + return $this; + } + + + + /** + * Checks if headers have been sent. + * @return bool + */ + public function isSent() + { + return headers_sent(); + } + + + + /** + * Return the value of the HTTP header. + * @param string + * @param mixed + * @return mixed + */ + public function getHeader($header, $default = NULL) + { + $header .= ':'; + $len = strlen($header); + foreach (headers_list() as $item) { + if (strncasecmp($item, $header, $len) === 0) { + return ltrim(substr($item, $len)); + } + } + return $default; + } + + + + /** + * Returns a list of headers to sent. + * @return array + */ + public function getHeaders() + { + $headers = array(); + foreach (headers_list() as $header) { + $a = strpos($header, ':'); + $headers[substr($header, 0, $a)] = (string) substr($header, $a + 2); + } + return $headers; + } + + + + /** + * Returns HTTP valid date format. + * @param string|int|DateTime + * @return string + */ + public static function date($time = NULL) + { + $time = Nette\DateTime::from($time); + $time->setTimezone(new \DateTimeZone('GMT')); + return $time->format('D, d M Y H:i:s \G\M\T'); + } + + + + /** + * @return void + */ + public function __destruct() + { + if (self::$fixIE && isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE + && in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE) + && $this->getHeader('Content-Type', 'text/html') === 'text/html' + ) { + echo Nette\Utils\Strings::random(2e3, " \t\r\n"); // sends invisible garbage for IE + self::$fixIE = FALSE; + } + } + + + + /** + * Sends a cookie. + * @param string name of the cookie + * @param string value + * @param string|int|DateTime expiration time, value 0 means "until the browser is closed" + * @param string + * @param string + * @param bool + * @param bool + * @return Response provides a fluent interface + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function setCookie($name, $value, $time, $path = NULL, $domain = NULL, $secure = NULL, $httpOnly = NULL) + { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot set cookie after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + + setcookie( + $name, + $value, + $time ? Nette\DateTime::from($time)->format('U') : 0, + $path === NULL ? $this->cookiePath : (string) $path, + $domain === NULL ? $this->cookieDomain : (string) $domain, + $secure === NULL ? $this->cookieSecure : (bool) $secure, + $httpOnly === NULL ? $this->cookieHttpOnly : (bool) $httpOnly + ); + + if (ini_get('suhosin.cookie.encrypt')) { + return $this; + } + + $flatten = array(); + foreach (headers_list() as $header) { + if (preg_match('#^Set-Cookie: .+?=#', $header, $m)) { + $flatten[$m[0]] = $header; + if (PHP_VERSION_ID < 50300) { // multiple deleting due PHP bug #61605 + header('Set-Cookie:'); + } else { + header_remove('Set-Cookie'); + } + } + } + foreach (array_values($flatten) as $key => $header) { + header($header, $key === 0); + } + + return $this; + } + + + + /** + * Deletes a cookie. + * @param string name of the cookie. + * @param string + * @param string + * @param bool + * @return void + * @throws Nette\InvalidStateException if HTTP headers have been sent + */ + public function deleteCookie($name, $path = NULL, $domain = NULL, $secure = NULL) + { + $this->setCookie($name, FALSE, 0, $path, $domain, $secure); + } + +} diff --git a/apigen/libs/Nette/Nette/Http/Session.php b/apigen/libs/Nette/Nette/Http/Session.php new file mode 100644 index 00000000000..c7d17b3bcef --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/Session.php @@ -0,0 +1,591 @@ + '', // must be disabled because PHP implementation is invalid + 'use_cookies' => 1, // must be enabled to prevent Session Hijacking and Fixation + 'use_only_cookies' => 1, // must be enabled to prevent Session Fixation + 'use_trans_sid' => 0, // must be disabled to prevent Session Hijacking and Fixation + + // cookies + 'cookie_lifetime' => 0, // until the browser is closed + 'cookie_path' => '/', // cookie is available within the entire domain + 'cookie_domain' => '', // cookie is available on current subdomain only + 'cookie_secure' => FALSE, // cookie is available on HTTP & HTTPS + 'cookie_httponly' => TRUE,// must be enabled to prevent Session Hijacking + + // other + 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME,// 3 hours + 'cache_limiter' => NULL, // (default "nocache", special value "\0") + 'cache_expire' => NULL, // (default "180") + 'hash_function' => NULL, // (default "0", means MD5) + 'hash_bits_per_character' => NULL, // (default "4") + ); + + /** @var IRequest */ + private $request; + + /** @var IResponse */ + private $response; + + + + public function __construct(IRequest $request, IResponse $response) + { + $this->request = $request; + $this->response = $response; + } + + + + /** + * Starts and initializes session data. + * @throws Nette\InvalidStateException + * @return void + */ + public function start() + { + if (self::$started) { + return; + } + + $this->configure($this->options); + + Nette\Diagnostics\Debugger::tryError(); + session_start(); + if (Nette\Diagnostics\Debugger::catchError($e) && !session_id()) { + @session_write_close(); // this is needed + throw new Nette\InvalidStateException('session_start(): ' . $e->getMessage(), 0, $e); + } + + self::$started = TRUE; + + /* structure: + __NF: Counter, BrowserKey, Data, Meta, Time + DATA: section->variable = data + META: section->variable = Timestamp, Browser, Version + */ + + unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old unused structures + + // initialize structures + $nf = & $_SESSION['__NF']; + if (empty($nf)) { // new session + $nf = array('C' => 0); + } else { + $nf['C']++; + } + + // session regenerate every 30 minutes + $nfTime = & $nf['Time']; + $time = time(); + if ($time - $nfTime > self::REGENERATE_INTERVAL) { + $this->regenerated = $this->regenerated || isset($nfTime); + $nfTime = $time; + } + + // browser closing detection + $browserKey = $this->request->getCookie('nette-browser'); + if (!$browserKey) { + $browserKey = Nette\Utils\Strings::random(); + } + $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; + $nf['B'] = $browserKey; + + // resend cookie + $this->sendCookie(); + + // process meta metadata + if (isset($nf['META'])) { + $now = time(); + // expire section variables + foreach ($nf['META'] as $section => $metadata) { + if (is_array($metadata)) { + foreach ($metadata as $variable => $value) { + if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T']) // whenBrowserIsClosed || Time + || (isset($nf['DATA'][$section][$variable]) && is_object($nf['DATA'][$section][$variable]) && (isset($value['V']) ? $value['V'] : NULL) // Version + != Nette\Reflection\ClassType::from($nf['DATA'][$section][$variable])->getAnnotation('serializationVersion')) // intentionally != + ) { + if ($variable === '') { // expire whole section + unset($nf['META'][$section], $nf['DATA'][$section]); + continue 2; + } + unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]); + } + } + } + } + } + + if ($this->regenerated) { + $this->regenerated = FALSE; + $this->regenerateId(); + } + + register_shutdown_function(array($this, 'clean')); + } + + + + /** + * Has been session started? + * @return bool + */ + public function isStarted() + { + return (bool) self::$started; + } + + + + /** + * Ends the current session and store session data. + * @return void + */ + public function close() + { + if (self::$started) { + $this->clean(); + session_write_close(); + self::$started = FALSE; + } + } + + + + /** + * Destroys all data registered to a session. + * @return void + */ + public function destroy() + { + if (!self::$started) { + throw new Nette\InvalidStateException('Session is not started.'); + } + + session_destroy(); + $_SESSION = NULL; + self::$started = FALSE; + if (!$this->response->isSent()) { + $params = session_get_cookie_params(); + $this->response->deleteCookie(session_name(), $params['path'], $params['domain'], $params['secure']); + } + } + + + + /** + * Does session exists for the current request? + * @return bool + */ + public function exists() + { + return self::$started || $this->request->getCookie($this->getName()) !== NULL; + } + + + + /** + * Regenerates the session ID. + * @throws Nette\InvalidStateException + * @return void + */ + public function regenerateId() + { + if (self::$started && !$this->regenerated) { + if (headers_sent($file, $line)) { + throw new Nette\InvalidStateException("Cannot regenerate session ID after HTTP headers have been sent" . ($file ? " (output started at $file:$line)." : ".")); + } + session_regenerate_id(TRUE); + session_write_close(); + $backup = $_SESSION; + session_start(); + $_SESSION = $backup; + } + $this->regenerated = TRUE; + } + + + + /** + * Returns the current session ID. Don't make dependencies, can be changed for each request. + * @return string + */ + public function getId() + { + return session_id(); + } + + + + /** + * Sets the session name to a specified one. + * @param string + * @return Session provides a fluent interface + */ + public function setName($name) + { + if (!is_string($name) || !preg_match('#[^0-9.][^.]*$#A', $name)) { + throw new Nette\InvalidArgumentException('Session name must be a string and cannot contain dot.'); + } + + session_name($name); + return $this->setOptions(array( + 'name' => $name, + )); + } + + + + /** + * Gets the session name. + * @return string + */ + public function getName() + { + return isset($this->options['name']) ? $this->options['name'] : session_name(); + } + + + + /********************* sections management ****************d*g**/ + + + + /** + * Returns specified session section. + * @param string + * @param string + * @return SessionSection + * @throws Nette\InvalidArgumentException + */ + public function getSection($section, $class = 'Nette\Http\SessionSection') + { + return new $class($this, $section); + } + + + + /** @deprecated */ + function getNamespace($section) + { + trigger_error(__METHOD__ . '() is deprecated; use getSection() instead.', E_USER_WARNING); + return $this->getSection($section); + } + + + + /** + * Checks if a session section exist and is not empty. + * @param string + * @return bool + */ + public function hasSection($section) + { + if ($this->exists() && !self::$started) { + $this->start(); + } + + return !empty($_SESSION['__NF']['DATA'][$section]); + } + + + + /** + * Iteration over all sections. + * @return \ArrayIterator + */ + public function getIterator() + { + if ($this->exists() && !self::$started) { + $this->start(); + } + + if (isset($_SESSION['__NF']['DATA'])) { + return new \ArrayIterator(array_keys($_SESSION['__NF']['DATA'])); + + } else { + return new \ArrayIterator; + } + } + + + + /** + * Cleans and minimizes meta structures. + * @return void + */ + public function clean() + { + if (!self::$started || empty($_SESSION)) { + return; + } + + $nf = & $_SESSION['__NF']; + if (isset($nf['META']) && is_array($nf['META'])) { + foreach ($nf['META'] as $name => $foo) { + if (empty($nf['META'][$name])) { + unset($nf['META'][$name]); + } + } + } + + if (empty($nf['META'])) { + unset($nf['META']); + } + + if (empty($nf['DATA'])) { + unset($nf['DATA']); + } + + if (empty($_SESSION)) { + //$this->destroy(); only when shutting down + } + } + + + + /********************* configuration ****************d*g**/ + + + + /** + * Sets session options. + * @param array + * @return Session provides a fluent interface + * @throws Nette\NotSupportedException + * @throws Nette\InvalidStateException + */ + public function setOptions(array $options) + { + if (self::$started) { + $this->configure($options); + } + $this->options = $options + $this->options; + if (!empty($options['auto_start'])) { + $this->start(); + } + return $this; + } + + + + /** + * Returns all session options. + * @return array + */ + public function getOptions() + { + return $this->options; + } + + + + /** + * Configurates session environment. + * @param array + * @return void + */ + private function configure(array $config) + { + $special = array('cache_expire' => 1, 'cache_limiter' => 1, 'save_path' => 1, 'name' => 1); + + foreach ($config as $key => $value) { + if (!strncmp($key, 'session.', 8)) { // back compatibility + $key = substr($key, 8); + } + $key = strtolower(preg_replace('#(.)(?=[A-Z])#', '$1_', $key)); + + if ($value === NULL || ini_get("session.$key") == $value) { // intentionally == + continue; + + } elseif (strncmp($key, 'cookie_', 7) === 0) { + if (!isset($cookie)) { + $cookie = session_get_cookie_params(); + } + $cookie[substr($key, 7)] = $value; + + } else { + if (defined('SID')) { + throw new Nette\InvalidStateException("Unable to set 'session.$key' to value '$value' when session has been started" . ($this->started ? "." : " by session.auto_start or session_start().")); + } + if (isset($special[$key])) { + $key = "session_$key"; + $key($value); + + } elseif (function_exists('ini_set')) { + ini_set("session.$key", $value); + + } elseif (!Nette\Framework::$iAmUsingBadHost) { + throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); + } + } + } + + if (isset($cookie)) { + session_set_cookie_params( + $cookie['lifetime'], $cookie['path'], $cookie['domain'], + $cookie['secure'], $cookie['httponly'] + ); + if (self::$started) { + $this->sendCookie(); + } + } + } + + + + /** + * Sets the amount of time allowed between requests before the session will be terminated. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @return Session provides a fluent interface + */ + public function setExpiration($time) + { + if (empty($time)) { + return $this->setOptions(array( + 'gc_maxlifetime' => self::DEFAULT_FILE_LIFETIME, + 'cookie_lifetime' => 0, + )); + + } else { + $time = Nette\DateTime::from($time)->format('U') - time(); + return $this->setOptions(array( + 'gc_maxlifetime' => $time, + 'cookie_lifetime' => $time, + )); + } + } + + + + /** + * Sets the session cookie parameters. + * @param string path + * @param string domain + * @param bool secure + * @return Session provides a fluent interface + */ + public function setCookieParameters($path, $domain = NULL, $secure = NULL) + { + return $this->setOptions(array( + 'cookie_path' => $path, + 'cookie_domain' => $domain, + 'cookie_secure' => $secure + )); + } + + + + /** + * Returns the session cookie parameters. + * @return array containing items: lifetime, path, domain, secure, httponly + */ + public function getCookieParameters() + { + return session_get_cookie_params(); + } + + + + /** @deprecated */ + function setCookieParams($path, $domain = NULL, $secure = NULL) + { + trigger_error(__METHOD__ . '() is deprecated; use setCookieParameters() instead.', E_USER_WARNING); + return $this->setCookieParameters($path, $domain, $secure); + } + + + + /** + * Sets path of the directory used to save session data. + * @return Session provides a fluent interface + */ + public function setSavePath($path) + { + return $this->setOptions(array( + 'save_path' => $path, + )); + } + + + + /** + * Sets user session storage. + * @return Session provides a fluent interface + */ + public function setStorage(ISessionStorage $storage) + { + if (self::$started) { + throw new Nette\InvalidStateException("Unable to set storage when session has been started."); + } + session_set_save_handler( + array($storage, 'open'), array($storage, 'close'), array($storage, 'read'), + array($storage, 'write'), array($storage, 'remove'), array($storage, 'clean') + ); + } + + + + /** + * Sends the session cookies. + * @return void + */ + private function sendCookie() + { + $cookie = $this->getCookieParameters(); + $this->response->setCookie( + session_name(), session_id(), + $cookie['lifetime'] ? $cookie['lifetime'] + time() : 0, + $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httponly'] + + )->setCookie( + 'nette-browser', $_SESSION['__NF']['B'], + Response::BROWSER, $cookie['path'], $cookie['domain'] + ); + } + +} diff --git a/apigen/libs/Nette/Nette/Http/SessionSection.php b/apigen/libs/Nette/Nette/Http/SessionSection.php new file mode 100644 index 00000000000..5fa0e4738de --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/SessionSection.php @@ -0,0 +1,273 @@ +session = $session; + $this->name = $name; + } + + + + /** + * Do not call directly. Use Session::getNamespace(). + */ + private function start() + { + if ($this->meta === FALSE) { + $this->session->start(); + $this->data = & $_SESSION['__NF']['DATA'][$this->name]; + $this->meta = & $_SESSION['__NF']['META'][$this->name]; + } + } + + + + /** + * Returns an iterator over all section variables. + * @return \ArrayIterator + */ + public function getIterator() + { + $this->start(); + if (isset($this->data)) { + return new \ArrayIterator($this->data); + } else { + return new \ArrayIterator; + } + } + + + + /** + * Sets a variable in this session section. + * @param string name + * @param mixed value + * @return void + */ + public function __set($name, $value) + { + $this->start(); + $this->data[$name] = $value; + if (is_object($value)) { + $this->meta[$name]['V'] = Nette\Reflection\ClassType::from($value)->getAnnotation('serializationVersion'); + } + } + + + + /** + * Gets a variable from this session section. + * @param string name + * @return mixed + */ + public function &__get($name) + { + $this->start(); + if ($this->warnOnUndefined && !array_key_exists($name, $this->data)) { + trigger_error("The variable '$name' does not exist in session section", E_USER_NOTICE); + } + + return $this->data[$name]; + } + + + + /** + * Determines whether a variable in this session section is set. + * @param string name + * @return bool + */ + public function __isset($name) + { + if ($this->session->exists()) { + $this->start(); + } + return isset($this->data[$name]); + } + + + + /** + * Unsets a variable in this session section. + * @param string name + * @return void + */ + public function __unset($name) + { + $this->start(); + unset($this->data[$name], $this->meta[$name]); + } + + + + /** + * Sets a variable in this session section. + * @param string name + * @param mixed value + * @return void + */ + public function offsetSet($name, $value) + { + $this->__set($name, $value); + } + + + + /** + * Gets a variable from this session section. + * @param string name + * @return mixed + */ + public function offsetGet($name) + { + return $this->__get($name); + } + + + + /** + * Determines whether a variable in this session section is set. + * @param string name + * @return bool + */ + public function offsetExists($name) + { + return $this->__isset($name); + } + + + + /** + * Unsets a variable in this session section. + * @param string name + * @return void + */ + public function offsetUnset($name) + { + $this->__unset($name); + } + + + + /** + * Sets the expiration of the section or specific variables. + * @param string|int|DateTime time, value 0 means "until the browser is closed" + * @param mixed optional list of variables / single variable to expire + * @return SessionSection provides a fluent interface + */ + public function setExpiration($time, $variables = NULL) + { + $this->start(); + if (empty($time)) { + $time = NULL; + $whenBrowserIsClosed = TRUE; + } else { + $time = Nette\DateTime::from($time)->format('U'); + $max = ini_get('session.gc_maxlifetime'); + if ($time - time() > $max + 3) { // bulgarian constant + trigger_error("The expiration time is greater than the session expiration $max seconds", E_USER_NOTICE); + } + $whenBrowserIsClosed = FALSE; + } + + if ($variables === NULL) { // to entire section + $this->meta['']['T'] = $time; + $this->meta['']['B'] = $whenBrowserIsClosed; + + } elseif (is_array($variables)) { // to variables + foreach ($variables as $variable) { + $this->meta[$variable]['T'] = $time; + $this->meta[$variable]['B'] = $whenBrowserIsClosed; + } + + } else { // to variable + $this->meta[$variables]['T'] = $time; + $this->meta[$variables]['B'] = $whenBrowserIsClosed; + } + return $this; + } + + + + /** + * Removes the expiration from the section or specific variables. + * @param mixed optional list of variables / single variable to expire + * @return void + */ + public function removeExpiration($variables = NULL) + { + $this->start(); + if ($variables === NULL) { + // from entire section + unset($this->meta['']['T'], $this->meta['']['B']); + + } elseif (is_array($variables)) { + // from variables + foreach ($variables as $variable) { + unset($this->meta[$variable]['T'], $this->meta[$variable]['B']); + } + } else { + unset($this->meta[$variables]['T'], $this->meta[$variable]['B']); + } + } + + + + /** + * Cancels the current session section. + * @return void + */ + public function remove() + { + $this->start(); + $this->data = NULL; + $this->meta = NULL; + } + +} diff --git a/apigen/libs/Nette/Nette/Http/Url.php b/apigen/libs/Nette/Nette/Http/Url.php new file mode 100644 index 00000000000..381fcd37d1c --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/Url.php @@ -0,0 +1,526 @@ + + * scheme user password host port basePath relativeUrl + * | | | | | | | + * /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\ + * http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl + * \__________________________/\____________/^\________/^\______/ + * | | | | + * authority path query fragment + * + * + * - authority: [user[:password]@]host[:port] + * - hostUrl: http://user:password@nette.org:8042 + * - basePath: /en/ (everything before relative URI not including the script name) + * - baseUrl: http://user:password@nette.org:8042/en/ + * - relativeUrl: manual.php + * + * @author David Grudl + * + * @property string $scheme + * @property string $user + * @property string $password + * @property string $host + * @property string $port + * @property string $path + * @property string $query + * @property string $fragment + * @property-read string $absoluteUrl + * @property-read string $authority + * @property-read string $hostUrl + * @property-read string $basePath + * @property-read string $baseUrl + * @property-read string $relativeUrl + */ +class Url extends Nette\FreezableObject +{ + /** @var array */ + public static $defaultPorts = array( + 'http' => 80, + 'https' => 443, + 'ftp' => 21, + 'news' => 119, + 'nntp' => 119, + ); + + /** @var string */ + private $scheme = ''; + + /** @var string */ + private $user = ''; + + /** @var string */ + private $pass = ''; + + /** @var string */ + private $host = ''; + + /** @var int */ + private $port = NULL; + + /** @var string */ + private $path = ''; + + /** @var string */ + private $query = ''; + + /** @var string */ + private $fragment = ''; + + + + /** + * @param string URL + * @throws Nette\InvalidArgumentException + */ + public function __construct($url = NULL) + { + if (is_string($url)) { + $parts = @parse_url($url); // @ - is escalated to exception + if ($parts === FALSE) { + throw new Nette\InvalidArgumentException("Malformed or unsupported URI '$url'."); + } + + foreach ($parts as $key => $val) { + $this->$key = $val; + } + + if (!$this->port && isset(self::$defaultPorts[$this->scheme])) { + $this->port = self::$defaultPorts[$this->scheme]; + } + + if ($this->path === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { + $this->path = '/'; + } + + } elseif ($url instanceof self) { + foreach ($this as $key => $val) { + $this->$key = $url->$key; + } + } + } + + + + /** + * Sets the scheme part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setScheme($value) + { + $this->updating(); + $this->scheme = (string) $value; + return $this; + } + + + + /** + * Returns the scheme part of URI. + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + + + /** + * Sets the user name part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setUser($value) + { + $this->updating(); + $this->user = (string) $value; + return $this; + } + + + + /** + * Returns the user name part of URI. + * @return string + */ + public function getUser() + { + return $this->user; + } + + + + /** + * Sets the password part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPassword($value) + { + $this->updating(); + $this->pass = (string) $value; + return $this; + } + + + + /** + * Returns the password part of URI. + * @return string + */ + public function getPassword() + { + return $this->pass; + } + + + + /** + * Sets the host part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setHost($value) + { + $this->updating(); + $this->host = (string) $value; + return $this; + } + + + + /** + * Returns the host part of URI. + * @return string + */ + public function getHost() + { + return $this->host; + } + + + + /** + * Sets the port part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPort($value) + { + $this->updating(); + $this->port = (int) $value; + return $this; + } + + + + /** + * Returns the port part of URI. + * @return string + */ + public function getPort() + { + return $this->port; + } + + + + /** + * Sets the path part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setPath($value) + { + $this->updating(); + $this->path = (string) $value; + return $this; + } + + + + /** + * Returns the path part of URI. + * @return string + */ + public function getPath() + { + return $this->path; + } + + + + /** + * Sets the query part of URI. + * @param string|array + * @return Url provides a fluent interface + */ + public function setQuery($value) + { + $this->updating(); + $this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); + return $this; + } + + + + /** + * Appends the query part of URI. + * @param string|array + * @return void + */ + public function appendQuery($value) + { + $this->updating(); + $value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value); + $this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value; + } + + + + /** + * Returns the query part of URI. + * @return string + */ + public function getQuery() + { + return $this->query; + } + + + + /** + * Sets the fragment part of URI. + * @param string + * @return Url provides a fluent interface + */ + public function setFragment($value) + { + $this->updating(); + $this->fragment = (string) $value; + return $this; + } + + + + /** + * Returns the fragment part of URI. + * @return string + */ + public function getFragment() + { + return $this->fragment; + } + + + + /** + * Returns the entire URI including query string and fragment. + * @return string + */ + public function getAbsoluteUrl() + { + return $this->scheme . '://' . $this->getAuthority() . $this->path + . ($this->query === '' ? '' : '?' . $this->query) + . ($this->fragment === '' ? '' : '#' . $this->fragment); + } + + + + /** + * Returns the [user[:pass]@]host[:port] part of URI. + * @return string + */ + public function getAuthority() + { + $authority = $this->host; + if ($this->port && isset(self::$defaultPorts[$this->scheme]) && $this->port !== self::$defaultPorts[$this->scheme]) { + $authority .= ':' . $this->port; + } + + if ($this->user !== '' && $this->scheme !== 'http' && $this->scheme !== 'https') { + $authority = $this->user . ($this->pass === '' ? '' : ':' . $this->pass) . '@' . $authority; + } + + return $authority; + } + + + + /** + * Returns the scheme and authority part of URI. + * @return string + */ + public function getHostUrl() + { + return $this->scheme . '://' . $this->getAuthority(); + } + + + + /** + * Returns the base-path. + * @return string + */ + public function getBasePath() + { + $pos = strrpos($this->path, '/'); + return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); + } + + + + /** + * Returns the base-URI. + * @return string + */ + public function getBaseUrl() + { + return $this->scheme . '://' . $this->getAuthority() . $this->getBasePath(); + } + + + + /** + * Returns the relative-URI. + * @return string + */ + public function getRelativeUrl() + { + return (string) substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl())); + } + + + + /** + * URI comparsion (this object must be in canonical form). + * @param string + * @return bool + */ + public function isEqual($url) + { + // compare host + path + $part = self::unescape(strtok($url, '?#'), '%/'); + if (strncmp($part, '//', 2) === 0) { // absolute URI without scheme + if ($part !== '//' . $this->getAuthority() . $this->path) { + return FALSE; + } + + } elseif (strncmp($part, '/', 1) === 0) { // absolute path + if ($part !== $this->path) { + return FALSE; + } + + } else { + if ($part !== $this->scheme . '://' . $this->getAuthority() . $this->path) { + return FALSE; + } + } + + // compare query strings + $part = preg_split('#[&;]#', self::unescape(strtr((string) strtok('?#'), '+', ' '), '%&;=+')); + sort($part); + $query = preg_split('#[&;]#', $this->query); + sort($query); + return $part === $query; + } + + + + /** + * Transform to canonical form. + * @return void + */ + public function canonicalize() + { + $this->updating(); + $this->path = $this->path === '' ? '/' : self::unescape($this->path, '%/'); + $this->host = strtolower(rawurldecode($this->host)); + $this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+'); + } + + + + /** + * @return string + */ + public function __toString() + { + return $this->getAbsoluteUrl(); + } + + + + /** + * Similar to rawurldecode, but preserve reserved chars encoded. + * @param string to decode + * @param string reserved characters + * @return string + */ + public static function unescape($s, $reserved = '%;/?:@&=+$,') + { + // reserved (@see RFC 2396) = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," + // within a path segment, the characters "/", ";", "=", "?" are reserved + // within a query component, the characters ";", "/", "?", ":", "@", "&", "=", "+", ",", "$" are reserved. + preg_match_all('#(?<=%)[a-f0-9][a-f0-9]#i', $s, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + foreach (array_reverse($matches) as $match) { + $ch = chr(hexdec($match[0][0])); + if (strpos($reserved, $ch) === FALSE) { + $s = substr_replace($s, $ch, $match[0][1] - 1, 3); + } + } + return $s; + } + + + + /** @deprecated */ + function getRelativeUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getRelativeUrl() instead.', E_USER_WARNING); + return $this->getRelativeUrl(); + } + + /** @deprecated */ + function getAbsoluteUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getAbsoluteUrl() instead.', E_USER_WARNING); + return $this->getAbsoluteUrl(); + } + + /** @deprecated */ + function getHostUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getHostUrl() instead.', E_USER_WARNING); + return $this->getHostUrl(); + } + + /** @deprecated */ + function getBaseUri() + { + trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::getBaseUrl() instead.', E_USER_WARNING); + return $this->getBaseUrl(); + } + +} diff --git a/apigen/libs/Nette/Nette/Http/UrlScript.php b/apigen/libs/Nette/Nette/Http/UrlScript.php new file mode 100644 index 00000000000..2aa641c7634 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/UrlScript.php @@ -0,0 +1,89 @@ + + * http://nette.org/admin/script.php/pathinfo/?name=param#fragment + * \_______________/\________/ + * | | + * scriptPath pathInfo + * + * + * - scriptPath: /admin/script.php (or simply /admin/ when script is directory index) + * - pathInfo: /pathinfo/ (additional path information) + * + * @author David Grudl + * + * @property string $scriptPath + * @property-read string $pathInfo + */ +class UrlScript extends Url +{ + /** @var string */ + private $scriptPath = '/'; + + + + /** + * Sets the script-path part of URI. + * @param string + * @return UrlScript provides a fluent interface + */ + public function setScriptPath($value) + { + $this->updating(); + $this->scriptPath = (string) $value; + return $this; + } + + + + /** + * Returns the script-path part of URI. + * @return string + */ + public function getScriptPath() + { + return $this->scriptPath; + } + + + + /** + * Returns the base-path. + * @return string + */ + public function getBasePath() + { + $pos = strrpos($this->scriptPath, '/'); + return $pos === FALSE ? '' : substr($this->path, 0, $pos + 1); + } + + + + /** + * Returns the additional path information. + * @return string + */ + public function getPathInfo() + { + return (string) substr($this->path, strlen($this->scriptPath)); + } + +} diff --git a/apigen/libs/Nette/Nette/Http/UserStorage.php b/apigen/libs/Nette/Nette/Http/UserStorage.php new file mode 100644 index 00000000000..13c93bf2651 --- /dev/null +++ b/apigen/libs/Nette/Nette/Http/UserStorage.php @@ -0,0 +1,221 @@ +sessionHandler = $sessionHandler; + } + + + + /** + * Sets the authenticated status of this user. + * @param bool + * @return UserStorage Provides a fluent interface + */ + public function setAuthenticated($state) + { + $section = $this->getSessionSection(TRUE); + $section->authenticated = (bool) $state; + + // Session Fixation defence + $this->sessionHandler->regenerateId(); + + if ($state) { + $section->reason = NULL; + $section->authTime = time(); // informative value + + } else { + $section->reason = self::MANUAL; + $section->authTime = NULL; + } + return $this; + } + + + + /** + * Is this user authenticated? + * @return bool + */ + public function isAuthenticated() + { + $session = $this->getSessionSection(FALSE); + return $session && $session->authenticated; + } + + + + /** + * Sets the user identity. + * @param IIdentity + * @return UserStorage Provides a fluent interface + */ + public function setIdentity(IIdentity $identity = NULL) + { + $this->getSessionSection(TRUE)->identity = $identity; + return $this; + } + + + + /** + * Returns current user identity, if any. + * @return Nette\Security\IIdentity|NULL + */ + public function getIdentity() + { + $session = $this->getSessionSection(FALSE); + return $session ? $session->identity : NULL; + } + + + + /** + * Changes namespace; allows more users to share a session. + * @param string + * @return UserStorage Provides a fluent interface + */ + public function setNamespace($namespace) + { + if ($this->namespace !== $namespace) { + $this->namespace = (string) $namespace; + $this->sessionSection = NULL; + } + return $this; + } + + + + /** + * Returns current namespace. + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + + + /** + * Enables log out after inactivity. + * @param string|int|DateTime Number of seconds or timestamp + * @param int Log out when the browser is closed | Clear the identity from persistent storage? + * @return UserStorage Provides a fluent interface + */ + public function setExpiration($time, $flags = 0) + { + $section = $this->getSessionSection(TRUE); + if ($time) { + $time = Nette\DateTime::from($time)->format('U'); + $section->expireTime = $time; + $section->expireDelta = $time - time(); + + } else { + unset($section->expireTime, $section->expireDelta); + } + + $section->expireIdentity = (bool) ($flags & self::CLEAR_IDENTITY); + $section->expireBrowser = (bool) ($flags & self::BROWSER_CLOSED); + $section->browserCheck = TRUE; + $section->setExpiration(0, 'browserCheck'); + $section->setExpiration($time, 'foo'); // time check + return $this; + } + + + + /** + * Why was user logged out? + * @return int + */ + public function getLogoutReason() + { + $session = $this->getSessionSection(FALSE); + return $session ? $session->reason : NULL; + } + + + + /** + * Returns and initializes $this->sessionSection. + * @return SessionSection + */ + protected function getSessionSection($need) + { + if ($this->sessionSection !== NULL) { + return $this->sessionSection; + } + + if (!$need && !$this->sessionHandler->exists()) { + return NULL; + } + + $this->sessionSection = $section = $this->sessionHandler->getSection('Nette.Http.UserStorage/' . $this->namespace); + + if (!$section->identity instanceof IIdentity || !is_bool($section->authenticated)) { + $section->remove(); + } + + if ($section->authenticated && $section->expireBrowser && !$section->browserCheck) { // check if browser was closed? + $section->reason = self::BROWSER_CLOSED; + $section->authenticated = FALSE; + if ($section->expireIdentity) { + unset($section->identity); + } + } + + if ($section->authenticated && $section->expireDelta > 0) { // check time expiration + if ($section->expireTime < time()) { + $section->reason = self::INACTIVITY; + $section->authenticated = FALSE; + if ($section->expireIdentity) { + unset($section->identity); + } + } + $section->expireTime = time() + $section->expireDelta; // sliding expiration + } + + if (!$section->authenticated) { + unset($section->expireTime, $section->expireDelta, $section->expireIdentity, + $section->expireBrowser, $section->browserCheck, $section->authTime); + } + + return $this->sessionSection; + } + +} diff --git a/apigen/libs/Nette/Nette/Iterators/CachingIterator.php b/apigen/libs/Nette/Nette/Iterators/CachingIterator.php new file mode 100644 index 00000000000..d97c8114186 --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/CachingIterator.php @@ -0,0 +1,266 @@ +getIterator(); + + } elseif (!$iterator instanceof \Iterator) { + $iterator = new \IteratorIterator($iterator); + } + + } else { + throw new Nette\InvalidArgumentException("Invalid argument passed to foreach resp. " . __CLASS__ . "; array or Traversable expected, " . (is_object($iterator) ? get_class($iterator) : gettype($iterator)) ." given."); + } + + parent::__construct($iterator, 0); + } + + + + /** + * Is the current element the first one? + * @param int grid width + * @return bool + */ + public function isFirst($width = NULL) + { + return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0); + } + + + + /** + * Is the current element the last one? + * @param int grid width + * @return bool + */ + public function isLast($width = NULL) + { + return !$this->hasNext() || ($width && ($this->counter % $width) === 0); + } + + + + /** + * Is the iterator empty? + * @return bool + */ + public function isEmpty() + { + return $this->counter === 0; + } + + + + /** + * Is the counter odd? + * @return bool + */ + public function isOdd() + { + return $this->counter % 2 === 1; + } + + + + /** + * Is the counter even? + * @return bool + */ + public function isEven() + { + return $this->counter % 2 === 0; + } + + + + /** + * Returns the counter. + * @return int + */ + public function getCounter() + { + return $this->counter; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + $inner = $this->getInnerIterator(); + if ($inner instanceof \Countable) { + return $inner->count(); + + } else { + throw new Nette\NotSupportedException('Iterator is not countable.'); + } + } + + + + /** + * Forwards to the next element. + * @return void + */ + public function next() + { + parent::next(); + if (parent::valid()) { + $this->counter++; + } + } + + + + /** + * Rewinds the Iterator. + * @return void + */ + public function rewind() + { + parent::rewind(); + $this->counter = parent::valid() ? 1 : 0; + } + + + + /** + * Returns the next key. + * @return mixed + */ + public function getNextKey() + { + return $this->getInnerIterator()->key(); + } + + + + /** + * Returns the next element. + * @return mixed + */ + public function getNextValue() + { + return $this->getInnerIterator()->current(); + } + + + + /********************* Nette\Object behaviour ****************d*g**/ + + + + /** + * Call to undefined method. + * @param string method name + * @param array arguments + * @return mixed + * @throws Nette\MemberAccessException + */ + public function __call($name, $args) + { + return Nette\ObjectMixin::call($this, $name, $args); + } + + + + /** + * Returns property value. Do not call directly. + * @param string property name + * @return mixed property value + * @throws Nette\MemberAccessException if the property is not defined. + */ + public function &__get($name) + { + return Nette\ObjectMixin::get($this, $name); + } + + + + /** + * Sets value of a property. Do not call directly. + * @param string property name + * @param mixed property value + * @return void + * @throws Nette\MemberAccessException if the property is not defined or is read-only + */ + public function __set($name, $value) + { + return Nette\ObjectMixin::set($this, $name, $value); + } + + + + /** + * Is property defined? + * @param string property name + * @return bool + */ + public function __isset($name) + { + return Nette\ObjectMixin::has($this, $name); + } + + + + /** + * Access to undeclared property. + * @param string property name + * @return void + * @throws Nette\MemberAccessException + */ + public function __unset($name) + { + Nette\ObjectMixin::remove($this, $name); + } + + +} diff --git a/apigen/libs/Nette/Nette/Iterators/Filter.php b/apigen/libs/Nette/Nette/Iterators/Filter.php new file mode 100644 index 00000000000..9470fa64c54 --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/Filter.php @@ -0,0 +1,47 @@ +callback = new Nette\Callback($callback); + } + + + + public function accept() + { + return $this->callback->invoke($this); + } + +} diff --git a/apigen/libs/Nette/Nette/Iterators/InstanceFilter.php b/apigen/libs/Nette/Nette/Iterators/InstanceFilter.php new file mode 100644 index 00000000000..dbbc376db0c --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/InstanceFilter.php @@ -0,0 +1,62 @@ +type = $type; + parent::__construct($iterator); + } + + + + /** + * Expose the current element of the inner iterator? + * @return bool + */ + public function accept() + { + return $this->current() instanceof $this->type; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/apigen/libs/Nette/Nette/Iterators/Mapper.php b/apigen/libs/Nette/Nette/Iterators/Mapper.php new file mode 100644 index 00000000000..5a891b57275 --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/Mapper.php @@ -0,0 +1,47 @@ +callback = new Nette\Callback($callback); + } + + + + public function current() + { + return $this->callback->invoke(parent::current(), parent::key()); + } + +} diff --git a/apigen/libs/Nette/Nette/Iterators/RecursiveFilter.php b/apigen/libs/Nette/Nette/Iterators/RecursiveFilter.php new file mode 100644 index 00000000000..6b43908f62d --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/RecursiveFilter.php @@ -0,0 +1,66 @@ +callback = $callback === NULL ? NULL : new Nette\Callback($callback); + $this->childrenCallback = $childrenCallback === NULL ? NULL : new Nette\Callback($childrenCallback); + } + + + + public function accept() + { + return $this->callback === NULL || $this->callback->invoke($this); + } + + + + public function hasChildren() + { + return $this->getInnerIterator()->hasChildren() + && ($this->childrenCallback === NULL || $this->childrenCallback->invoke($this)); + } + + + + public function getChildren() + { + return new static($this->getInnerIterator()->getChildren(), $this->callback, $this->childrenCallback); + } + +} diff --git a/apigen/libs/Nette/Nette/Iterators/Recursor.php b/apigen/libs/Nette/Nette/Iterators/Recursor.php new file mode 100644 index 00000000000..a300eaea972 --- /dev/null +++ b/apigen/libs/Nette/Nette/Iterators/Recursor.php @@ -0,0 +1,60 @@ +current(); + return ($obj instanceof \IteratorAggregate && $obj->getIterator() instanceof \RecursiveIterator) + || $obj instanceof \RecursiveIterator; + } + + + + /** + * The sub-iterator for the current element. + * @return \RecursiveIterator + */ + public function getChildren() + { + $obj = $this->current(); + return $obj instanceof \IteratorAggregate ? $obj->getIterator() : $obj; + } + + + + /** + * Returns the count of elements. + * @return int + */ + public function count() + { + return iterator_count($this); + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Compiler.php b/apigen/libs/Nette/Nette/Latte/Compiler.php new file mode 100644 index 00000000000..2d72152df8d --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Compiler.php @@ -0,0 +1,530 @@ + IMacro[]] */ + private $macros; + + /** @var \SplObjectStorage */ + private $macroHandlers; + + /** @var HtmlNode[] */ + private $htmlNodes = array(); + + /** @var MacroNode[] */ + private $macroNodes = array(); + + /** @var string[] */ + private $attrCodes = array(); + + /** @var string */ + private $contentType; + + /** @var array [context, subcontext] */ + private $context; + + /** @var string */ + private $templateId; + + /** Context-aware escaping content types */ + const CONTENT_HTML = 'html', + CONTENT_XHTML = 'xhtml', + CONTENT_XML = 'xml', + CONTENT_JS = 'js', + CONTENT_CSS = 'css', + CONTENT_ICAL = 'ical', + CONTENT_TEXT = 'text'; + + /** @internal Context-aware escaping HTML contexts */ + const CONTEXT_COMMENT = 'comment', + CONTEXT_SINGLE_QUOTED = "'", + CONTEXT_DOUBLE_QUOTED = '"'; + + + public function __construct() + { + $this->macroHandlers = new \SplObjectStorage; + } + + + + /** + * Adds new macro. + * @param string + * @return Compiler provides a fluent interface + */ + public function addMacro($name, IMacro $macro) + { + $this->macros[$name][] = $macro; + $this->macroHandlers->attach($macro); + return $this; + } + + + + /** + * Compiles tokens to PHP code. + * @param Token[] + * @return string + */ + public function compile(array $tokens) + { + $this->templateId = Strings::random(); + $this->tokens = $tokens; + $output = ''; + $this->output = & $output; + $this->htmlNodes = $this->macroNodes = array(); + $this->setContentType($this->defaultContentType); + + foreach ($this->macroHandlers as $handler) { + $handler->initialize($this); + } + + try { + foreach ($tokens as $this->position => $token) { + if ($token->type === Token::TEXT) { + $this->output .= $token->text; + + } elseif ($token->type === Token::MACRO_TAG) { + $isRightmost = !isset($tokens[$this->position + 1]) + || substr($tokens[$this->position + 1]->text, 0, 1) === "\n"; + $this->writeMacro($token->name, $token->value, $token->modifiers, $isRightmost); + + } elseif ($token->type === Token::HTML_TAG_BEGIN) { + $this->processHtmlTagBegin($token); + + } elseif ($token->type === Token::HTML_TAG_END) { + $this->processHtmlTagEnd($token); + + } elseif ($token->type === Token::HTML_ATTRIBUTE) { + $this->processHtmlAttribute($token); + + } elseif ($token->type === Token::COMMENT) { + $this->processComment($token); + } + } + } catch (CompileException $e) { + $e->sourceLine = $token->line; + throw $e; + } + + + foreach ($this->htmlNodes as $htmlNode) { + if (!empty($htmlNode->macroAttrs)) { + throw new CompileException("Missing end tag name> for macro-attribute " . Parser::N_PREFIX + . implode(' and ' . Parser::N_PREFIX, array_keys($htmlNode->macroAttrs)) . ".", 0, $token->line); + } + } + + $prologs = $epilogs = ''; + foreach ($this->macroHandlers as $handler) { + $res = $handler->finalize(); + $handlerName = get_class($handler); + $prologs .= empty($res[0]) ? '' : ""; + $epilogs = (empty($res[1]) ? '' : "") . $epilogs; + } + $output = ($prologs ? $prologs . "\n" : '') . $output . $epilogs; + + if ($this->macroNodes) { + throw new CompileException("There are unclosed macros.", 0, $token->line); + } + + $output = $this->expandTokens($output); + return $output; + } + + + + /** + * @return Compiler provides a fluent interface + */ + public function setContentType($type) + { + $this->contentType = $type; + $this->context = NULL; + return $this; + } + + + + /** + * @return string + */ + public function getContentType() + { + return $this->contentType; + } + + + + /** + * @return Compiler provides a fluent interface + */ + public function setContext($context, $sub = NULL) + { + $this->context = array($context, $sub); + return $this; + } + + + + /** + * @return array [context, subcontext] + */ + public function getContext() + { + return $this->context; + } + + + + /** + * @return string + */ + public function getTemplateId() + { + return $this->templateId; + } + + + + /** + * Returns current line number. + * @return int + */ + public function getLine() + { + return $this->tokens ? $this->tokens[$this->position]->line : NULL; + } + + + + public function expandTokens($s) + { + return strtr($s, $this->attrCodes); + } + + + + private function processHtmlTagBegin(Token $token) + { + if ($token->closing) { + do { + $htmlNode = array_pop($this->htmlNodes); + if (!$htmlNode) { + $htmlNode = new HtmlNode($token->name); + } + if (strcasecmp($htmlNode->name, $token->name) === 0) { + break; + } + if ($htmlNode->macroAttrs) { + throw new CompileException("Unexpected name>.", 0, $token->line); + } + } while (TRUE); + $this->htmlNodes[] = $htmlNode; + $htmlNode->closing = TRUE; + $htmlNode->offset = strlen($this->output); + $this->setContext(NULL); + + } elseif ($token->text === '') { + $this->output .= $token->text; + $this->setContext(NULL); + return; + } + + $htmlNode = end($this->htmlNodes); + $isEmpty = !$htmlNode->closing && (Strings::contains($token->text, '/') || $htmlNode->isEmpty); + + if ($isEmpty && in_array($this->contentType, array(self::CONTENT_HTML, self::CONTENT_XHTML))) { // auto-correct + $token->text = preg_replace('#^.*>#', $this->contentType === self::CONTENT_XHTML ? ' />' : '>', $token->text); + } + + if (empty($htmlNode->macroAttrs)) { + $this->output .= $token->text; + } else { + $code = substr($this->output, $htmlNode->offset) . $token->text; + $this->output = substr($this->output, 0, $htmlNode->offset); + $this->writeAttrsMacro($code, $htmlNode); + if ($isEmpty) { + $htmlNode->closing = TRUE; + $this->writeAttrsMacro('', $htmlNode); + } + } + + if ($isEmpty) { + $htmlNode->closing = TRUE; + } + + if (!$htmlNode->closing && (strcasecmp($htmlNode->name, 'script') === 0 || strcasecmp($htmlNode->name, 'style') === 0)) { + $this->setContext(strcasecmp($htmlNode->name, 'style') ? self::CONTENT_JS : self::CONTENT_CSS); + } else { + $this->setContext(NULL); + if ($htmlNode->closing) { + array_pop($this->htmlNodes); + } + } + } + + + + private function processHtmlAttribute(Token $token) + { + $htmlNode = end($this->htmlNodes); + if (Strings::startsWith($token->name, Parser::N_PREFIX)) { + $name = substr($token->name, strlen(Parser::N_PREFIX)); + if (isset($htmlNode->macroAttrs[$name])) { + throw new CompileException("Found multiple macro-attributes $token->name.", 0, $token->line); + } + $htmlNode->macroAttrs[$name] = $token->value; + + } else { + $htmlNode->attrs[$token->name] = TRUE; + $this->output .= $token->text; + if ($token->value) { // quoted + $context = NULL; + if (strncasecmp($token->name, 'on', 2) === 0) { + $context = self::CONTENT_JS; + } elseif ($token->name === 'style') { + $context = self::CONTENT_CSS; + } + $this->setContext($token->value, $context); + } + } + } + + + + private function processComment(Token $token) + { + $isLeftmost = trim(substr($this->output, strrpos("\n$this->output", "\n"))) === ''; + if (!$isLeftmost) { + $this->output .= substr($token->text, strlen(rtrim($token->text, "\n"))); + } + } + + + + /********************* macros ****************d*g**/ + + + + /** + * Generates code for {macro ...} to the output. + * @param string + * @param string + * @param string + * @param bool + * @return MacroNode + */ + public function writeMacro($name, $args = NULL, $modifiers = NULL, $isRightmost = FALSE, HtmlNode $htmlNode = NULL, $prefix = NULL) + { + if ($name[0] === '/') { // closing + $node = end($this->macroNodes); + + if (!$node || ("/$node->name" !== $name && '/' !== $name) || $modifiers + || ($args && $node->args && !Strings::startsWith("$node->args ", "$args ")) + ) { + $name .= $args ? ' ' : ''; + throw new CompileException("Unexpected macro {{$name}{$args}{$modifiers}}" + . ($node ? ", expecting {/$node->name}" . ($args && $node->args ? " or eventually {/$node->name $node->args}" : '') : '')); + } + + array_pop($this->macroNodes); + if (!$node->args) { + $node->setArgs($args); + } + + $isLeftmost = $node->content ? trim(substr($this->output, strrpos("\n$this->output", "\n"))) === '' : FALSE; + + $node->closing = TRUE; + $node->macro->nodeClosed($node); + + $this->output = & $node->saved[0]; + $this->writeCode($node->openingCode, $this->output, $node->saved[1]); + $this->writeCode($node->closingCode, $node->content, $isRightmost, $isLeftmost); + $this->output .= $node->content; + + } else { // opening + $node = $this->expandMacro($name, $args, $modifiers, $htmlNode, $prefix); + if ($node->isEmpty) { + $this->writeCode($node->openingCode, $this->output, $isRightmost); + + } else { + $this->macroNodes[] = $node; + $node->saved = array(& $this->output, $isRightmost); + $this->output = & $node->content; + } + } + return $node; + } + + + + private function writeCode($code, & $output, $isRightmost, $isLeftmost = NULL) + { + if ($isRightmost) { + $leftOfs = strrpos("\n$output", "\n"); + $isLeftmost = $isLeftmost === NULL ? trim(substr($output, $leftOfs)) === '' : $isLeftmost; + if ($isLeftmost && substr($code, 0, 11) !== ' remove indentation + } elseif (substr($code, -2) === '?>') { + $code .= "\n"; // double newline to avoid newline eating by PHP + } + } + $output .= $code; + } + + + + /** + * Generates code for macro to the output. + * @param string + * @return void + */ + public function writeAttrsMacro($code, HtmlNode $htmlNode) + { + $attrs = $htmlNode->macroAttrs; + $left = $right = array(); + $attrCode = ''; + + foreach ($this->macros as $name => $foo) { + $attrName = MacroNode::PREFIX_INNER . "-$name"; + if (isset($attrs[$attrName])) { + if ($htmlNode->closing) { + $left[] = array("/$name", '', MacroNode::PREFIX_INNER); + } else { + array_unshift($right, array($name, $attrs[$attrName], MacroNode::PREFIX_INNER)); + } + unset($attrs[$attrName]); + } + } + + foreach (array_reverse($this->macros) as $name => $foo) { + $attrName = MacroNode::PREFIX_TAG . "-$name"; + if (isset($attrs[$attrName])) { + $left[] = array($name, $attrs[$attrName], MacroNode::PREFIX_TAG); + array_unshift($right, array("/$name", '', MacroNode::PREFIX_TAG)); + unset($attrs[$attrName]); + } + } + + foreach ($this->macros as $name => $foo) { + if (isset($attrs[$name])) { + if ($htmlNode->closing) { + $right[] = array("/$name", '', NULL); + } else { + array_unshift($left, array($name, $attrs[$name], NULL)); + } + unset($attrs[$name]); + } + } + + if ($attrs) { + throw new CompileException("Unknown macro-attribute " . Parser::N_PREFIX + . implode(' and ' . Parser::N_PREFIX, array_keys($attrs))); + } + + if (!$htmlNode->closing) { + $htmlNode->attrCode = & $this->attrCodes[$uniq = ' n:' . Nette\Utils\Strings::random()]; + $code = substr_replace($code, $uniq, strrpos($code, '/>') ?: strrpos($code, '>'), 0); + } + + foreach ($left as $item) { + $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode, $item[2]); + if ($node->closing || $node->isEmpty) { + $htmlNode->attrCode .= $node->attrCode; + if ($node->isEmpty) { + unset($htmlNode->macroAttrs[$node->name]); + } + } + } + + $this->output .= $code; + + foreach ($right as $item) { + $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode); + if ($node->closing) { + $htmlNode->attrCode .= $node->attrCode; + } + } + + if ($right && substr($this->output, -2) === '?>') { + $this->output .= "\n"; + } + } + + + + /** + * Expands macro and returns node & code. + * @param string + * @param string + * @param string + * @return MacroNode + */ + public function expandMacro($name, $args, $modifiers = NULL, HtmlNode $htmlNode = NULL, $prefix = NULL) + { + if (empty($this->macros[$name])) { + $cdata = $this->htmlNodes && in_array(strtolower(end($this->htmlNodes)->name), array('script', 'style')); + throw new CompileException("Unknown macro {{$name}}" . ($cdata ? " (in JavaScript or CSS, try to put a space after bracket.)" : '')); + } + foreach (array_reverse($this->macros[$name]) as $macro) { + $node = new MacroNode($macro, $name, $args, $modifiers, $this->macroNodes ? end($this->macroNodes) : NULL, $htmlNode, $prefix); + if ($macro->nodeOpened($node) !== FALSE) { + return $node; + } + } + throw new CompileException("Unhandled macro {{$name}}"); + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Engine.php b/apigen/libs/Nette/Nette/Latte/Engine.php new file mode 100644 index 00000000000..468c9cdfc27 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Engine.php @@ -0,0 +1,77 @@ +parser = new Parser; + $this->compiler = new Compiler; + $this->compiler->defaultContentType = Compiler::CONTENT_XHTML; + + Macros\CoreMacros::install($this->compiler); + $this->compiler->addMacro('cache', new Macros\CacheMacro($this->compiler)); + Macros\UIMacros::install($this->compiler); + Macros\FormMacros::install($this->compiler); + } + + + + /** + * Invokes filter. + * @param string + * @return string + */ + public function __invoke($s) + { + return $this->compiler->compile($this->parser->parse($s)); + } + + + + /** + * @return Parser + */ + public function getParser() + { + return $this->parser; + } + + + + /** + * @return Compiler + */ + public function getCompiler() + { + return $this->compiler; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/HtmlNode.php b/apigen/libs/Nette/Nette/Latte/HtmlNode.php new file mode 100644 index 00000000000..b4e5858e4a5 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/HtmlNode.php @@ -0,0 +1,53 @@ +name = $name; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/IMacro.php b/apigen/libs/Nette/Nette/Latte/IMacro.php new file mode 100644 index 00000000000..55917b70a31 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/IMacro.php @@ -0,0 +1,50 @@ +macro = $macro; + $this->name = (string) $name; + $this->modifiers = (string) $modifiers; + $this->parentNode = $parentNode; + $this->htmlNode = $htmlNode; + $this->prefix = $prefix; + $this->tokenizer = new MacroTokenizer($this->args); + $this->data = new \stdClass; + $this->setArgs($args); + } + + + + public function setArgs($args) + { + $this->args = (string) $args; + $this->tokenizer->tokenize($this->args); + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/MacroTokenizer.php b/apigen/libs/Nette/Nette/Latte/MacroTokenizer.php new file mode 100644 index 00000000000..d1fb7bf88e9 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/MacroTokenizer.php @@ -0,0 +1,69 @@ + '\s+', + self::T_COMMENT => '(?s)/\*.*?\*/', + self::T_STRING => Parser::RE_STRING, + self::T_KEYWORD => '(?:true|false|null|and|or|xor|clone|new|instanceof|return|continue|break|[A-Z_][A-Z0-9_]{2,})(?![\w\pL_])', // keyword or const + self::T_CAST => '\((?:expand|string|array|int|integer|float|bool|boolean|object)\)', // type casting + self::T_VARIABLE => '\$[\w\pL_]+', + self::T_NUMBER => '[+-]?[0-9]+(?:\.[0-9]+)?(?:e[0-9]+)?', + self::T_SYMBOL => '[\w\pL_]+(?:-[\w\pL_]+)*', + self::T_CHAR => '::|=>|[^"\']', // =>, any char except quotes + ), 'u'); + $this->ignored = array(self::T_COMMENT, self::T_WHITESPACE); + $this->tokenize($input); + } + + + + /** + * Reads single token (optionally delimited by comma) from string. + * @param string + * @return string + */ + public function fetchWord() + { + $word = $this->fetchUntil(self::T_WHITESPACE, ','); + $this->fetch(','); + $this->fetchAll(self::T_WHITESPACE, self::T_COMMENT); + return $word; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Macros/CacheMacro.php b/apigen/libs/Nette/Nette/Latte/Macros/CacheMacro.php new file mode 100644 index 00000000000..9da7142be1d --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Macros/CacheMacro.php @@ -0,0 +1,132 @@ +used = FALSE; + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + if ($this->used) { + return array('Nette\Latte\Macros\CacheMacro::initRuntime($template, $_g);'); + } + } + + + + /** + * New node is found. + * @return bool + */ + public function nodeOpened(Latte\MacroNode $node) + { + $this->used = TRUE; + $node->isEmpty = FALSE; + $node->openingCode = Latte\PhpWriter::using($node) + ->write('caches, %node.array?)) { ?>', + Nette\Utils\Strings::random() + ); + } + + + + /** + * Node is closed. + * @return void + */ + public function nodeClosed(Latte\MacroNode $node) + { + $node->closingCode = 'tmp = array_pop($_g->caches); if (!$_l->tmp instanceof stdClass) $_l->tmp->end(); } ?>'; + } + + + + /********************* run-time helpers ****************d*g**/ + + + + /** + * @return void + */ + public static function initRuntime(Nette\Templating\FileTemplate $template, \stdClass $global) + { + if (!empty($global->caches)) { + end($global->caches)->dependencies[Nette\Caching\Cache::FILES][] = $template->getFile(); + } + } + + + + /** + * Starts the output cache. Returns Nette\Caching\OutputHelper object if buffering was started. + * @param Nette\Caching\IStorage + * @param string + * @param Nette\Caching\OutputHelper[] + * @param array + * @return Nette\Caching\OutputHelper + */ + public static function createCache(Nette\Caching\IStorage $cacheStorage, $key, & $parents, array $args = NULL) + { + if ($args) { + if (array_key_exists('if', $args) && !$args['if']) { + return $parents[] = (object) NULL; + } + $key = array_merge(array($key), array_intersect_key($args, range(0, count($args)))); + } + if ($parents) { + end($parents)->dependencies[Nette\Caching\Cache::ITEMS][] = $key; + } + + $cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Templating.Cache'); + if ($helper = $cache->start($key)) { + if (isset($args['expire'])) { + $args['expiration'] = $args['expire']; // back compatibility + } + $helper->dependencies = array( + Nette\Caching\Cache::TAGS => isset($args['tags']) ? $args['tags'] : NULL, + Nette\Caching\Cache::EXPIRATION => isset($args['expiration']) ? $args['expiration'] : '+ 7 days', + ); + $parents[] = $helper; + } + return $helper; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Macros/CoreMacros.php b/apigen/libs/Nette/Nette/Latte/Macros/CoreMacros.php new file mode 100644 index 00000000000..35ce3fc1639 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Macros/CoreMacros.php @@ -0,0 +1,415 @@ + value} set template parameter + * - {default var => value} set default template parameter + * - {dump $var} + * - {debugbreak} + * - {l} {r} to display { } + * + * @author David Grudl + */ +class CoreMacros extends MacroSet +{ + + + public static function install(Latte\Compiler $compiler) + { + $me = new static($compiler); + + $me->addMacro('if', array($me, 'macroIf'), array($me, 'macroEndIf')); + $me->addMacro('elseif', 'elseif (%node.args):'); + $me->addMacro('else', array($me, 'macroElse')); + $me->addMacro('ifset', 'if (isset(%node.args)):', 'endif'); + $me->addMacro('elseifset', 'elseif (isset(%node.args)):'); + + $me->addMacro('foreach', '', array($me, 'macroEndForeach')); + $me->addMacro('for', 'for (%node.args):', 'endfor'); + $me->addMacro('while', 'while (%node.args):', 'endwhile'); + $me->addMacro('continueIf', 'if (%node.args) continue'); + $me->addMacro('breakIf', 'if (%node.args) break'); + $me->addMacro('first', 'if ($iterator->isFirst(%node.args)):', 'endif'); + $me->addMacro('last', 'if ($iterator->isLast(%node.args)):', 'endif'); + $me->addMacro('sep', 'if (!$iterator->isLast(%node.args)):', 'endif'); + + $me->addMacro('var', array($me, 'macroVar')); + $me->addMacro('assign', array($me, 'macroVar')); // deprecated + $me->addMacro('default', array($me, 'macroVar')); + $me->addMacro('dump', array($me, 'macroDump')); + $me->addMacro('debugbreak', array($me, 'macroDebugbreak')); + $me->addMacro('l', '?>{addMacro('r', '?>}addMacro('_', array($me, 'macroTranslate'), array($me, 'macroTranslate')); + $me->addMacro('=', array($me, 'macroExpr')); + $me->addMacro('?', array($me, 'macroExpr')); + + $me->addMacro('capture', array($me, 'macroCapture'), array($me, 'macroCaptureEnd')); + $me->addMacro('include', array($me, 'macroInclude')); + $me->addMacro('use', array($me, 'macroUse')); + + $me->addMacro('class', NULL, NULL, array($me, 'macroClass')); + $me->addMacro('attr', array($me, 'macroOldAttr'), '', array($me, 'macroAttr')); + $me->addMacro('href', NULL); // TODO: placeholder + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + return array('list($_l, $_g) = Nette\Latte\Macros\CoreMacros::initRuntime($template, ' + . var_export($this->getCompiler()->getTemplateId(), TRUE) . ')'); + } + + + + /********************* macros ****************d*g**/ + + + + /** + * {if ...} + */ + public function macroIf(MacroNode $node, PhpWriter $writer) + { + if ($node->data->capture = ($node->args === '')) { + return 'ob_start()'; + } + if ($node->prefix === $node::PREFIX_TAG) { + return $writer->write($node->htmlNode->closing ? 'if (array_pop($_l->ifs)):' : 'if ($_l->ifs[] = (%node.args)):'); + } + return $writer->write('if (%node.args):'); + } + + + + /** + * {/if ...} + */ + public function macroEndIf(MacroNode $node, PhpWriter $writer) + { + if ($node->data->capture) { + if ($node->args === '') { + throw new CompileException('Missing condition in {if} macro.'); + } + return $writer->write('if (%node.args) ' + . (isset($node->data->else) ? '{ ob_end_clean(); ob_end_flush(); }' : 'ob_end_flush();') + . ' else ' + . (isset($node->data->else) ? '{ $_else = ob_get_contents(); ob_end_clean(); ob_end_clean(); echo $_else; }' : 'ob_end_clean();') + ); + } + return 'endif'; + } + + + + /** + * {else} + */ + public function macroElse(MacroNode $node, PhpWriter $writer) + { + $ifNode = $node->parentNode; + if ($ifNode && $ifNode->name === 'if' && $ifNode->data->capture) { + if (isset($ifNode->data->else)) { + throw new CompileException("Macro {if} supports only one {else}."); + } + $ifNode->data->else = TRUE; + return 'ob_start()'; + } + return 'else:'; + } + + + + /** + * {_$var |modifiers} + */ + public function macroTranslate(MacroNode $node, PhpWriter $writer) + { + if ($node->closing) { + return $writer->write('echo %modify($template->translate(ob_get_clean()))'); + + } elseif ($node->isEmpty = ($node->args !== '')) { + return $writer->write('echo %modify($template->translate(%node.args))'); + + } else { + return 'ob_start()'; + } + } + + + + /** + * {include "file" [,] [params]} + */ + public function macroInclude(MacroNode $node, PhpWriter $writer) + { + $code = $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + $template->getParameters(), $_l->templates[%var])', + $this->getCompiler()->getTemplateId()); + + if ($node->modifiers) { + return $writer->write('echo %modify(%raw->__toString(TRUE))', $code); + } else { + return $code . '->render()'; + } + } + + + + /** + * {use class MacroSet} + */ + public function macroUse(MacroNode $node, PhpWriter $writer) + { + Nette\Callback::create($node->tokenizer->fetchWord(), 'install') + ->invoke($this->getCompiler()) + ->initialize(); + } + + + + /** + * {capture $variable} + */ + public function macroCapture(MacroNode $node, PhpWriter $writer) + { + $variable = $node->tokenizer->fetchWord(); + if (substr($variable, 0, 1) !== '$') { + throw new CompileException("Invalid capture block variable '$variable'"); + } + $node->data->variable = $variable; + return 'ob_start()'; + } + + + + /** + * {/capture} + */ + public function macroCaptureEnd(MacroNode $node, PhpWriter $writer) + { + return $node->data->variable . $writer->write(" = %modify(ob_get_clean())"); + } + + + + /** + * {foreach ...} + */ + public function macroEndForeach(MacroNode $node, PhpWriter $writer) + { + if (preg_match('#\W(\$iterator|include|require|get_defined_vars)\W#', $this->getCompiler()->expandTokens($node->content))) { + $node->openingCode = 'its[] = new Nette\Iterators\CachingIterator(' + . preg_replace('#(.*)\s+as\s+#i', '$1) as ', $writer->formatArgs(), 1) . '): ?>'; + $node->closingCode = 'its); $iterator = end($_l->its) ?>'; + } else { + $node->openingCode = 'formatArgs() . '): ?>'; + $node->closingCode = ''; + } + } + + + + /** + * n:class="..." + */ + public function macroClass(MacroNode $node, PhpWriter $writer) + { + return $writer->write('if ($_l->tmp = array_filter(%node.array)) echo \' class="\' . %escape(implode(" ", array_unique($_l->tmp))) . \'"\''); + } + + + + /** + * n:attr="..." + */ + public function macroAttr(MacroNode $node, PhpWriter $writer) + { + return $writer->write('echo Nette\Utils\Html::el(NULL, %node.array)->attributes()'); + } + + + + /** + * {attr ...} + * @deprecated + */ + public function macroOldAttr(MacroNode $node) + { + return Nette\Utils\Strings::replace($node->args . ' ', '#\)\s+#', ')->'); + } + + + + /** + * {dump ...} + */ + public function macroDump(MacroNode $node, PhpWriter $writer) + { + $args = $writer->formatArgs(); + return 'Nette\Diagnostics\Debugger::barDump(' . ($node->args ? "array(" . $writer->write('%var', $args) . " => $args)" : 'get_defined_vars()') + . ', "Template " . str_replace(dirname(dirname($template->getFile())), "\xE2\x80\xA6", $template->getFile()))'; + } + + + + /** + * {debugbreak ...} + */ + public function macroDebugbreak(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->args == NULL ? '' : 'if (!(%node.args)); else') + . 'if (function_exists("debugbreak")) debugbreak(); elseif (function_exists("xdebug_break")) xdebug_break()'); + } + + + + /** + * {var ...} + * {default ...} + */ + public function macroVar(MacroNode $node, PhpWriter $writer) + { + $out = ''; + $var = TRUE; + $tokenizer = $writer->preprocess(); + while ($token = $tokenizer->fetchToken()) { + if ($var && ($token['type'] === Latte\MacroTokenizer::T_SYMBOL || $token['type'] === Latte\MacroTokenizer::T_VARIABLE)) { + if ($node->name === 'default') { + $out .= "'" . ltrim($token['value'], "$") . "'"; + } else { + $out .= '$' . ltrim($token['value'], "$"); + } + $var = NULL; + + } elseif (($token['value'] === '=' || $token['value'] === '=>') && $token['depth'] === 0) { + $out .= $node->name === 'default' ? '=>' : '='; + $var = FALSE; + + } elseif ($token['value'] === ',' && $token['depth'] === 0) { + $out .= $node->name === 'default' ? ',' : ';'; + $var = TRUE; + + } elseif ($var === NULL && $node->name === 'default' && $token['type'] !== Latte\MacroTokenizer::T_WHITESPACE) { + throw new CompileException("Unexpected '$token[value]' in {default $node->args}"); + + } else { + $out .= $writer->canQuote($tokenizer) ? "'$token[value]'" : $token['value']; + } + } + return $node->name === 'default' ? "extract(array($out), EXTR_SKIP)" : $out; + } + + + + /** + * {= ...} + * {? ...} + */ + public function macroExpr(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->name === '?' ? '' : 'echo ') . '%modify(%node.args)'); + } + + + + /********************* run-time helpers ****************d*g**/ + + + + /** + * Includes subtemplate. + * @param mixed included file name or template + * @param array parameters + * @param Nette\Templating\ITemplate current template + * @return Nette\Templating\Template + */ + public static function includeTemplate($destination, array $params, Nette\Templating\ITemplate $template) + { + if ($destination instanceof Nette\Templating\ITemplate) { + $tpl = $destination; + + } elseif ($destination == NULL) { // intentionally == + throw new Nette\InvalidArgumentException("Template file name was not specified."); + + } elseif ($template instanceof Nette\Templating\IFileTemplate) { + if (substr($destination, 0, 1) !== '/' && substr($destination, 1, 1) !== ':') { + $destination = dirname($template->getFile()) . '/' . $destination; + } + $tpl = clone $template; + $tpl->setFile($destination); + + } else { + throw new Nette\NotSupportedException('Macro {include "filename"} is supported only with Nette\Templating\IFileTemplate.'); + } + + $tpl->setParameters($params); // interface? + return $tpl; + } + + + + /** + * Initializes local & global storage in template. + * @param + * @param string + * @return \stdClass + */ + public static function initRuntime(Nette\Templating\ITemplate $template, $templateId) + { + // local storage + if (isset($template->_l)) { + $local = $template->_l; + unset($template->_l); + } else { + $local = (object) NULL; + } + $local->templates[$templateId] = $template; + + // global storage + if (!isset($template->_g)) { + $template->_g = (object) NULL; + } + + return array($local, $template->_g); + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Macros/FormMacros.php b/apigen/libs/Nette/Nette/Latte/Macros/FormMacros.php new file mode 100644 index 00000000000..c2d26c4c2bf --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Macros/FormMacros.php @@ -0,0 +1,137 @@ +addMacro('form', + 'Nette\Latte\Macros\FormMacros::renderFormBegin($form = $_form = (is_object(%node.word) ? %node.word : $_control[%node.word]), %node.array)', + 'Nette\Latte\Macros\FormMacros::renderFormEnd($_form)'); + $me->addMacro('label', array($me, 'macroLabel'), '?>addMacro('input', 'echo $_form[%node.word]->getControl()->addAttributes(%node.array)', NULL, array($me, 'macroAttrInput')); + $me->addMacro('formContainer', '$_formStack[] = $_form; $formContainer = $_form = $_form[%node.word]', '$_form = array_pop($_formStack)'); + } + + + + /********************* macros ****************d*g**/ + + + /** + * {label ...} and optionally {/label} + */ + public function macroLabel(MacroNode $node, PhpWriter $writer) + { + $cmd = 'if ($_label = $_form[%node.word]->getLabel()) echo $_label->addAttributes(%node.array)'; + if ($node->isEmpty = (substr($node->args, -1) === '/')) { + $node->setArgs(substr($node->args, 0, -1)); + return $writer->write($cmd); + } else { + return $writer->write($cmd . '->startTag()'); + } + } + + + + /** + * n:input + */ + public function macroAttrInput(MacroNode $node, PhpWriter $writer) + { + if ($node->htmlNode->attrs) { + $reset = array_fill_keys(array_keys($node->htmlNode->attrs), NULL); + return $writer->write('echo $_form[%node.word]->getControl()->addAttributes(%var)->attributes()', $reset); + } + return $writer->write('echo $_form[%node.word]->getControl()->attributes()'); + } + + + + /********************* run-time writers ****************d*g**/ + + + + /** + * Renders form begin. + * @return void + */ + public static function renderFormBegin(Form $form, array $attrs) + { + $el = $form->getElementPrototype(); + $el->action = (string) $el->action; + $el = clone $el; + if (strcasecmp($form->getMethod(), 'get') === 0) { + list($el->action) = explode('?', $el->action, 2); + } + echo $el->addAttributes($attrs)->startTag(); + } + + + + /** + * Renders form end. + * @return string + */ + public static function renderFormEnd(Form $form) + { + $s = ''; + if (strcasecmp($form->getMethod(), 'get') === 0) { + $url = explode('?', $form->getElementPrototype()->action, 2); + if (isset($url[1])) { + foreach (preg_split('#[;&]#', $url[1]) as $param) { + $parts = explode('=', $param, 2); + $name = urldecode($parts[0]); + if (!isset($form[$name])) { + $s .= Nette\Utils\Html::el('input', array('type' => 'hidden', 'name' => $name, 'value' => urldecode($parts[1]))); + } + } + } + } + + foreach ($form->getComponents(TRUE, 'Nette\Forms\Controls\HiddenField') as $control) { + if (!$control->getOption('rendered')) { + $s .= $control->getControl(); + } + } + + if (iterator_count($form->getComponents(TRUE, 'Nette\Forms\Controls\TextInput')) < 2) { + $s .= ''; + } + + echo ($s ? "
$s
\n" : '') . $form->getElementPrototype()->endTag() . "\n"; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Macros/MacroSet.php b/apigen/libs/Nette/Nette/Latte/Macros/MacroSet.php new file mode 100644 index 00000000000..7b1d089f589 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Macros/MacroSet.php @@ -0,0 +1,143 @@ +compiler = $compiler; + } + + + + public function addMacro($name, $begin, $end = NULL, $attr = NULL) + { + $this->macros[$name] = array($begin, $end, $attr); + $this->compiler->addMacro($name, $this); + return $this; + } + + + + public static function install(Latte\Compiler $compiler) + { + return new static($compiler); + } + + + + /** + * Initializes before template parsing. + * @return void + */ + public function initialize() + { + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + } + + + + /** + * New node is found. + * @return bool + */ + public function nodeOpened(MacroNode $node) + { + if ($this->macros[$node->name][2] && $node->htmlNode) { + $node->isEmpty = TRUE; + $this->compiler->setContext(Latte\Compiler::CONTEXT_DOUBLE_QUOTED); + $res = $this->compile($node, $this->macros[$node->name][2]); + $this->compiler->setContext(NULL); + if (!$node->attrCode) { + $node->attrCode = ""; + } + } else { + $node->isEmpty = !isset($this->macros[$node->name][1]); + $res = $this->compile($node, $this->macros[$node->name][0]); + if (!$node->openingCode) { + $node->openingCode = ""; + } + } + return $res !== FALSE; + } + + + + /** + * Node is closed. + * @return void + */ + public function nodeClosed(MacroNode $node) + { + $res = $this->compile($node, $this->macros[$node->name][1]); + if (!$node->closingCode) { + $node->closingCode = ""; + } + } + + + + /** + * Generates code. + * @return string + */ + private function compile(MacroNode $node, $def) + { + $node->tokenizer->reset(); + $writer = Latte\PhpWriter::using($node, $this->compiler); + if (is_string($def)/*5.2* && substr($def, 0, 1) !== "\0"*/) { + return $writer->write($def); + } else { + return Nette\Callback::create($def)->invoke($node, $writer); + } + } + + + + /** + * @return Latte\Compiler + */ + public function getCompiler() + { + return $this->compiler; + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Macros/UIMacros.php b/apigen/libs/Nette/Nette/Latte/Macros/UIMacros.php new file mode 100644 index 00000000000..b20ae4c5401 --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Macros/UIMacros.php @@ -0,0 +1,520 @@ +addMacro('include', array($me, 'macroInclude')); + $me->addMacro('includeblock', array($me, 'macroIncludeBlock')); + $me->addMacro('extends', array($me, 'macroExtends')); + $me->addMacro('layout', array($me, 'macroExtends')); + $me->addMacro('block', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('define', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('snippet', array($me, 'macroBlock'), array($me, 'macroBlockEnd')); + $me->addMacro('ifset', array($me, 'macroIfset'), 'endif'); + + $me->addMacro('widget', array($me, 'macroControl')); // deprecated - use control + $me->addMacro('control', array($me, 'macroControl')); + + $me->addMacro('href', NULL, NULL, function(MacroNode $node, PhpWriter $writer) use ($me) { + return ' ?> href="macroLink($node, $writer) . ' ?>"addMacro('plink', array($me, 'macroLink')); + $me->addMacro('link', array($me, 'macroLink')); + $me->addMacro('ifCurrent', array($me, 'macroIfCurrent'), 'endif'); // deprecated; use n:class="$presenter->linkCurrent ? ..." + + $me->addMacro('contentType', array($me, 'macroContentType')); + $me->addMacro('status', array($me, 'macroStatus')); + } + + + + /** + * Initializes before template parsing. + * @return void + */ + public function initialize() + { + $this->namedBlocks = array(); + $this->extends = NULL; + } + + + + /** + * Finishes template parsing. + * @return array(prolog, epilog) + */ + public function finalize() + { + // try close last block + try { + $this->getCompiler()->writeMacro('/block'); + } catch (CompileException $e) { + } + + $epilog = $prolog = array(); + + if ($this->namedBlocks) { + foreach ($this->namedBlocks as $name => $code) { + $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name); + $snippet = $name[0] === '_'; + $prolog[] = "//\n// block $name\n//\n" + . "if (!function_exists(\$_l->blocks[" . var_export($name, TRUE) . "][] = '$func')) { " + . "function $func(\$_l, \$_args) { " + . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v') // PHP bug #46873 + . ($snippet ? '; $_control->validateControl(' . var_export(substr($name, 1), TRUE) . ')' : '') + . "\n?>$codenamedBlocks || $this->extends) { + $prolog[] = "// template extending and snippets support"; + + $prolog[] = '$_l->extends = ' + . ($this->extends ? $this->extends : 'empty($template->_extended) && isset($_control) && $_control instanceof Nette\Application\UI\Presenter ? $_control->findLayoutTemplateFile() : NULL') + . '; $template->_extended = $_extended = TRUE;'; + + $prolog[] = ' +if ($_l->extends) { + ' . ($this->namedBlocks ? 'ob_start();' : 'return Nette\Latte\Macros\CoreMacros::includeTemplate($_l->extends, get_defined_vars(), $template)->render();') . ' + +} elseif (!empty($_control->snippetMode)) { + return Nette\Latte\Macros\UIMacros::renderSnippets($_control, $_l, get_defined_vars()); +}'; + } else { + $prolog[] = ' +// snippets support +if (!empty($_control->snippetMode)) { + return Nette\Latte\Macros\UIMacros::renderSnippets($_control, $_l, get_defined_vars()); +}'; + } + + return array(implode("\n\n", $prolog), implode("\n", $epilog)); + } + + + + /********************* macros ****************d*g**/ + + + + /** + * {include #block} + */ + public function macroInclude(MacroNode $node, PhpWriter $writer) + { + $destination = $node->tokenizer->fetchWord(); // destination [,] [params] + if (substr($destination, 0, 1) !== '#') { + return FALSE; + } + + $destination = ltrim($destination, '#'); + $parent = $destination === 'parent'; + if ($destination === 'parent' || $destination === 'this') { + for ($item = $node->parentNode; $item && $item->name !== 'block' && !isset($item->data->name); $item = $item->parentNode); + if (!$item) { + throw new CompileException("Cannot include $destination block outside of any block."); + } + $destination = $item->data->name; + } + + $name = Strings::contains($destination, '$') ? $destination : var_export($destination, TRUE); + if (isset($this->namedBlocks[$destination]) && !$parent) { + $cmd = "call_user_func(reset(\$_l->blocks[$name]), \$_l, %node.array? + get_defined_vars())"; + } else { + $cmd = 'Nette\Latte\Macros\UIMacros::callBlock' . ($parent ? 'Parent' : '') . "(\$_l, $name, %node.array? + " . ($parent ? 'get_defined_vars' : '$template->getParameters') . '())'; + } + + if ($node->modifiers) { + return $writer->write("ob_start(); $cmd; echo %modify(ob_get_clean())"); + } else { + return $writer->write($cmd); + } + } + + + + /** + * {includeblock "file"} + */ + public function macroIncludeBlock(MacroNode $node, PhpWriter $writer) + { + return $writer->write('Nette\Latte\Macros\CoreMacros::includeTemplate(%node.word, %node.array? + get_defined_vars(), $_l->templates[%var])->render()', + $this->getCompiler()->getTemplateId()); + } + + + + /** + * {extends auto | none | $var | "file"} + */ + public function macroExtends(MacroNode $node, PhpWriter $writer) + { + if (!$node->args) { + throw new CompileException("Missing destination in {extends}"); + } + if (!empty($node->parentNode)) { + throw new CompileException("{extends} must be placed outside any macro."); + } + if ($this->extends !== NULL) { + throw new CompileException("Multiple {extends} declarations are not allowed."); + } + if ($node->args === 'none') { + $this->extends = 'FALSE'; + } elseif ($node->args === 'auto') { + $this->extends = '$_presenter->findLayoutTemplateFile()'; + } else { + $this->extends = $writer->write('%node.word%node.args'); + } + return; + } + + + + /** + * {block [[#]name]} + * {snippet [name [,]] [tag]} + * {define [#]name} + */ + public function macroBlock(MacroNode $node, PhpWriter $writer) + { + $name = $node->tokenizer->fetchWord(); + + if ($node->name === 'block' && $name === FALSE) { // anonymous block + return $node->modifiers === '' ? '' : 'ob_start()'; + } + + $node->data->name = $name = ltrim($name, '#'); + if ($name == NULL) { + if ($node->name !== 'snippet') { + throw new CompileException("Missing block name."); + } + + } elseif (Strings::contains($name, '$')) { // dynamic block/snippet + if ($node->name === 'snippet') { + for ($parent = $node->parentNode; $parent && $parent->name !== 'snippet'; $parent = $parent->parentNode); + if (!$parent) { + throw new CompileException("Dynamic snippets are allowed only inside static snippet."); + } + $parent->data->dynamic = TRUE; + $node->data->leave = TRUE; + $node->closingCode = ""; + + if ($node->htmlNode) { + $node->attrCode = $writer->write("getSnippetId({$writer->formatWord($name)})) . '\"' ?>"); + return $writer->write('ob_start()'); + } + $tag = trim($node->tokenizer->fetchWord(), '<>'); + $tag = $tag ? $tag : 'div'; + $node->closingCode .= "\n"; + return $writer->write("?>\n<$tag id=\"getSnippetId({$writer->formatWord($name)}) ?>\">data->leave = TRUE; + $fname = $writer->formatWord($name); + $node->closingCode = "name === 'define' ? '' : "call_user_func(reset(\$_l->blocks[$fname]), \$_l, get_defined_vars())") . " ?>"; + $func = '_lb' . substr(md5($this->getCompiler()->getTemplateId() . $name), 0, 10) . '_' . preg_replace('#[^a-z0-9_]#i', '_', $name); + return "\n\n//\n// block $name\n//\n" + . "if (!function_exists(\$_l->blocks[$fname]['{$this->getCompiler()->getTemplateId()}'] = '$func')) { " + . "function $func(\$_l, \$_args) { " + . (PHP_VERSION_ID > 50208 ? 'extract($_args)' : 'foreach ($_args as $__k => $__v) $$__k = $__v'); // PHP bug #46873 + } + } + + // static block/snippet + if ($node->name === 'snippet') { + $node->data->name = $name = '_' . $name; + } + + if (isset($this->namedBlocks[$name])) { + throw new CompileException("Cannot redeclare static block '$name'"); + } + + $prolog = $this->namedBlocks ? '' : "if (\$_l->extends) { ob_end_clean(); return Nette\\Latte\\Macros\\CoreMacros::includeTemplate(\$_l->extends, get_defined_vars(), \$template)->render(); }\n"; + $top = empty($node->parentNode); + $this->namedBlocks[$name] = TRUE; + + $include = 'call_user_func(reset($_l->blocks[%var]), $_l, ' . ($node->name === 'snippet' ? '$template->getParameters()' : 'get_defined_vars()') . ')'; + if ($node->modifiers) { + $include = "ob_start(); $include; echo %modify(ob_get_clean())"; + } + + if ($node->name === 'snippet') { + if ($node->htmlNode) { + $node->attrCode = $writer->write('getSnippetId(%var) . \'"\' ?>', (string) substr($name, 1)); + return $writer->write($prolog . $include, $name); + } + $tag = trim($node->tokenizer->fetchWord(), '<>'); + $tag = $tag ? $tag : 'div'; + return $writer->write("$prolog ?>\n<$tag id=\"getSnippetId(%var) ?>\">\nname === 'define') { + return $prolog; + + } else { + return $writer->write($prolog . $include, $name); + } + } + + + + /** + * {/block} + * {/snippet} + * {/define} + */ + public function macroBlockEnd(MacroNode $node, PhpWriter $writer) + { + if (isset($node->data->name)) { // block, snippet, define + if ($node->name === 'snippet' && $node->htmlNode && !$node->prefix // n:snippet -> n:inner-snippet + && preg_match("#^.*? n:\w+>\n?#s", $node->content, $m1) && preg_match("#[ \t]*<[^<]+$#sD", $node->content, $m2)) + { + $node->openingCode = $m1[0] . $node->openingCode; + $node->content = substr($node->content, strlen($m1[0]), -strlen($m2[0])); + $node->closingCode .= $m2[0]; + } + + if (empty($node->data->leave)) { + if (!empty($node->data->dynamic)) { + $node->content .= ''; + } + $this->namedBlocks[$node->data->name] = $tmp = rtrim(ltrim($node->content, "\n"), " \t"); + $node->content = substr_replace($node->content, $node->openingCode . "\n", strspn($node->content, "\n"), strlen($tmp)); + $node->openingCode = ""; + } + + } elseif ($node->modifiers) { // anonymous block with modifier + return $writer->write('echo %modify(ob_get_clean())'); + } + } + + + + /** + * {ifset #block} + */ + public function macroIfset(MacroNode $node, PhpWriter $writer) + { + if (!Strings::contains($node->args, '#')) { + return FALSE; + } + $list = array(); + while (($name = $node->tokenizer->fetchWord()) !== FALSE) { + $list[] = $name[0] === '#' ? '$_l->blocks["' . substr($name, 1) . '"]' : $name; + } + return 'if (isset(' . implode(', ', $list) . ')):'; + } + + + + /** + * {control name[:method] [params]} + */ + public function macroControl(MacroNode $node, PhpWriter $writer) + { + $pair = $node->tokenizer->fetchWord(); + if ($pair === FALSE) { + throw new CompileException("Missing control name in {control}"); + } + $pair = explode(':', $pair, 2); + $name = $writer->formatWord($pair[0]); + $method = isset($pair[1]) ? ucfirst($pair[1]) : ''; + $method = Strings::match($method, '#^\w*$#') ? "render$method" : "{\"render$method\"}"; + $param = $writer->formatArray(); + if (!Strings::contains($node->args, '=>')) { + $param = substr($param, 6, -1); // removes array() + } + return ($name[0] === '$' ? "if (is_object($name)) \$_ctrl = $name; else " : '') + . '$_ctrl = $_control->getComponent(' . $name . '); ' + . 'if ($_ctrl instanceof Nette\Application\UI\IRenderable) $_ctrl->validateControl(); ' + . "\$_ctrl->$method($param)"; + } + + + + /** + * {link destination [,] [params]} + * {plink destination [,] [params]} + * n:href="destination [,] [params]" + */ + public function macroLink(MacroNode $node, PhpWriter $writer) + { + return $writer->write('echo %escape(%modify(' . ($node->name === 'plink' ? '$_presenter' : '$_control') . '->link(%node.word, %node.array?)))'); + } + + + + /** + * {ifCurrent destination [,] [params]} + */ + public function macroIfCurrent(MacroNode $node, PhpWriter $writer) + { + return $writer->write(($node->args ? 'try { $_presenter->link(%node.word, %node.array?); } catch (Nette\Application\UI\InvalidLinkException $e) {}' : '') + . '; if ($_presenter->getLastCreatedRequestFlag("current")):'); + } + + + + /** + * {contentType ...} + */ + public function macroContentType(MacroNode $node, PhpWriter $writer) + { + if (Strings::contains($node->args, 'xhtml')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XHTML); + + } elseif (Strings::contains($node->args, 'html')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_HTML); + + } elseif (Strings::contains($node->args, 'xml')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_XML); + + } elseif (Strings::contains($node->args, 'javascript')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_JS); + + } elseif (Strings::contains($node->args, 'css')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_CSS); + + } elseif (Strings::contains($node->args, 'calendar')) { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_ICAL); + + } else { + $this->getCompiler()->setContentType(Latte\Compiler::CONTENT_TEXT); + } + + // temporary solution + if (Strings::contains($node->args, '/')) { + return $writer->write('$netteHttpResponse->setHeader("Content-Type", %var)', $node->args); + } + } + + + + /** + * {status ...} + */ + public function macroStatus(MacroNode $node, PhpWriter $writer) + { + return $writer->write((substr($node->args, -1) === '?' ? 'if (!$netteHttpResponse->isSent()) ' : '') . + '$netteHttpResponse->setCode(%var)', (int) $node->args + ); + } + + + + /********************* run-time writers ****************d*g**/ + + + + /** + * Calls block. + * @return void + */ + public static function callBlock(\stdClass $context, $name, array $params) + { + if (empty($context->blocks[$name])) { + throw new Nette\InvalidStateException("Cannot include undefined block '$name'."); + } + $block = reset($context->blocks[$name]); + $block($context, $params); + } + + + + /** + * Calls parent block. + * @return void + */ + public static function callBlockParent(\stdClass $context, $name, array $params) + { + if (empty($context->blocks[$name]) || ($block = next($context->blocks[$name])) === FALSE) { + throw new Nette\InvalidStateException("Cannot include undefined parent block '$name'."); + } + $block($context, $params); + } + + + + public static function renderSnippets(Nette\Application\UI\Control $control, \stdClass $local, array $params) + { + $control->snippetMode = FALSE; + $payload = $control->getPresenter()->getPayload(); + if (isset($local->blocks)) { + foreach ($local->blocks as $name => $function) { + if ($name[0] !== '_' || !$control->isControlInvalid(substr($name, 1))) { + continue; + } + ob_start(); + $function = reset($function); + $snippets = $function($local, $params); + $payload->snippets[$id = $control->getSnippetId(substr($name, 1))] = ob_get_clean(); + if ($snippets) { + $payload->snippets += $snippets; + unset($payload->snippets[$id]); + } + } + } + $control->snippetMode = TRUE; + if ($control instanceof Nette\Application\UI\IRenderable) { + $queue = array($control); + do { + foreach (array_shift($queue)->getComponents() as $child) { + if ($child instanceof Nette\Application\UI\IRenderable) { + if ($child->isControlInvalid()) { + $child->snippetMode = TRUE; + $child->render(); + $child->snippetMode = FALSE; + } + } elseif ($child instanceof Nette\ComponentModel\IContainer) { + $queue[] = $child; + } + } + } while ($queue); + } + } + +} diff --git a/apigen/libs/Nette/Nette/Latte/Parser.php b/apigen/libs/Nette/Nette/Latte/Parser.php new file mode 100644 index 00000000000..df2f8b5824a --- /dev/null +++ b/apigen/libs/Nette/Nette/Latte/Parser.php @@ -0,0 +1,405 @@ + array('\\{(?![\\s\'"{}])', '\\}'), // {...} + 'double' => array('\\{\\{(?![\\s\'"{}])', '\\}\\}'), // {{...}} + 'asp' => array('<%\s*', '\s*%>'), /* <%...%> */ + 'python' => array('\\{[{%]\s*', '\s*[%}]\\}'), // {% ... %} | {{ ... }} + 'off' => array('[^\x00-\xFF]', ''), + ); + + /** @var string */ + private $macroRe; + + /** @var string source template */ + private $input; + + /** @var Token[] */ + private $output; + + /** @var int position on source template */ + private $offset; + + /** @var array */ + private $context; + + /** @var string */ + private $lastHtmlTag; + + /** @var string used by filter() */ + private $syntaxEndTag; + + /** @var bool */ + private $xmlMode; + + /** @internal states */ + const CONTEXT_TEXT = 'text', + CONTEXT_CDATA = 'cdata', + CONTEXT_TAG = 'tag', + CONTEXT_ATTRIBUTE = 'attribute', + CONTEXT_NONE = 'none', + CONTEXT_COMMENT = 'comment'; + + + + /** + * Process all {macros} and . + * @param string + * @return array + */ + public function parse($input) + { + if (substr($input, 0, 3) === "\xEF\xBB\xBF") { // BOM + $input = substr($input, 3); + } + if (!Strings::checkEncoding($input)) { + throw new Nette\InvalidArgumentException('Template is not valid UTF-8 stream.'); + } + $input = str_replace("\r\n", "\n", $input); + $this->input = $input; + $this->output = array(); + $this->offset = 0; + + $this->setSyntax($this->defaultSyntax); + $this->setContext(self::CONTEXT_TEXT); + $this->lastHtmlTag = $this->syntaxEndTag = NULL; + + while ($this->offset < strlen($input)) { + $matches = $this->{"context".$this->context[0]}(); + + if (!$matches) { // EOF + break; + + } elseif (!empty($matches['comment'])) { // {* *} + $this->addToken(Token::COMMENT, $matches[0]); + + } elseif (!empty($matches['macro'])) { // {macro} + $token = $this->addToken(Token::MACRO_TAG, $matches[0]); + list($token->name, $token->value, $token->modifiers) = $this->parseMacroTag($matches['macro']); + } + + $this->filter(); + } + + if ($this->offset < strlen($input)) { + $this->addToken(Token::TEXT, substr($this->input, $this->offset)); + } + return $this->output; + } + + + + /** + * Handles CONTEXT_TEXT. + */ + private function contextText() + { + $matches = $this->match('~ + (?:(?<=\n|^)[ \t]*)?<(?P/?)(?P[a-z0-9:]+)| ## begin of HTML tag !--)| ## begin of HTML comment $levels['#'] + $top = 0 + 1 = 1 -->

...

+ '*' => 1, + '=' => 2, + '-' => 3, + ); + + /** @var array used ID's */ + private $usedID; + + + + public function __construct($texy) + { + $this->texy = $texy; + + $texy->addHandler('heading', array($this, 'solve')); + $texy->addHandler('beforeParse', array($this, 'beforeParse')); + $texy->addHandler('afterParse', array($this, 'afterParse')); + + $texy->registerBlockPattern( + array($this, 'patternUnderline'), + '#^(\S.*)'.TEXY_MODIFIER_H.'?\n' + . '(\#{3,}|\*{3,}|={3,}|-{3,})$#mU', + 'heading/underlined' + ); + + $texy->registerBlockPattern( + array($this, 'patternSurround'), + '#^(\#{2,}+|={2,}+)(.+)'.TEXY_MODIFIER_H.'?()$#mU', + 'heading/surrounded' + ); + } + + + + public function beforeParse() + { + $this->title = NULL; + $this->usedID = array(); + $this->TOC = array(); + } + + + + /** + * @param Texy + * @param TexyHtml + * @param bool + * @return void + */ + public function afterParse($texy, $DOM, $isSingleLine) + { + if ($isSingleLine) return; + + if ($this->balancing === self::DYNAMIC) { + $top = $this->top; + $map = array(); + $min = 100; + foreach ($this->TOC as $item) + { + $level = $item['level']; + if ($item['type'] === 'surrounded') { + $min = min($level, $min); + $top = $this->top - $min; + + } elseif ($item['type'] === 'underlined') { + $map[$level] = $level; + } + } + + asort($map); + $map = array_flip(array_values($map)); + } + + foreach ($this->TOC as $key => $item) + { + if ($this->balancing === self::DYNAMIC) { + if ($item['type'] === 'surrounded') { + $level = $item['level'] + $top; + + } elseif ($item['type'] === 'underlined') { + $level = $map[$item['level']] + $this->top; + + } else { + $level = $item['level']; + } + + $item['el']->setName('h' . min(6, max(1, $level))); + $this->TOC[$key]['level'] = $level; + } + + if ($this->generateID && empty($item['el']->attrs['id'])) { + $title = trim($item['el']->toText($this->texy)); + if ($title !== '') { + $this->TOC[$key]['title'] = $title; + $id = $this->idPrefix . Texy::webalize($title); + $counter = ''; + if (isset($this->usedID[$id . $counter])) { + $counter = 2; + while (isset($this->usedID[$id . '-' . $counter])) $counter++; + $id .= '-' . $counter; + } + $this->usedID[$id] = TRUE; + $item['el']->attrs['id'] = $id; + } + } + } + + // document title + if ($this->title === NULL && count($this->TOC)) { + $item = reset($this->TOC); + $this->title = isset($item['title']) ? $item['title'] : trim($item['el']->toText($this->texy)); + } + } + + + + /** + * Callback for underlined heading. + * + * Heading .(title)[class]{style}> + * ------------------------------- + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternUnderline($parser, $matches) + { + list(, $mContent, $mMod, $mLine) = $matches; + // $matches: + // [1] => ... + // [2] => .(title)[class]{style}<> + // [3] => ... + + $mod = new TexyModifier($mMod); + $level = $this->levels[$mLine[0]]; + return $this->texy->invokeAroundHandlers('heading', $parser, array($level, $mContent, $mod, FALSE)); + } + + + + /** + * Callback for surrounded heading. + * + * ### Heading .(title)[class]{style}> + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternSurround($parser, $matches) + { + list(, $mLine, $mContent, $mMod) = $matches; + // [1] => ### + // [2] => ... + // [3] => .(title)[class]{style}<> + + $mod = new TexyModifier($mMod); + $level = min(7, max(2, strlen($mLine))); + $level = $this->moreMeansHigher ? 7 - $level : $level - 2; + $mContent = rtrim($mContent, $mLine[0] . ' '); + return $this->texy->invokeAroundHandlers('heading', $parser, array($level, $mContent, $mod, TRUE)); + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param int 0..5 + * @param string + * @param TexyModifier + * @param bool + * @return TexyHtml + */ + public function solve($invocation, $level, $content, $mod, $isSurrounded) + { + // as fixed balancing, for block/texysource & correct decorating + $el = TexyHtml::el('h' . min(6, max(1, $level + $this->top))); + $mod->decorate($this->texy, $el); + + $el->parseLine($this->texy, trim($content)); + + $this->TOC[] = array( + 'el' => $el, + 'level' => $level, + 'type' => $isSurrounded ? 'surrounded' : 'underlined', + ); + + return $el; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyHorizLineModule.php b/apigen/libs/Texy/texy/modules/TexyHorizLineModule.php new file mode 100644 index 00000000000..f39522221e2 --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyHorizLineModule.php @@ -0,0 +1,86 @@ + NULL, + '*' => NULL, + ); + + + + public function __construct($texy) + { + $this->texy = $texy; + + $texy->addHandler('horizline', array($this, 'solve')); + + $texy->registerBlockPattern( + array($this, 'pattern'), + '#^(\*{3,}|-{3,})\ *'.TEXY_MODIFIER.'?()$#mU', + 'horizline' + ); + } + + + + /** + * Callback for: -------. + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml + */ + public function pattern($parser, $matches) + { + list(, $mType, $mMod) = $matches; + // [1] => --- + // [2] => .(title)[class]{style}<> + + $mod = new TexyModifier($mMod); + return $this->texy->invokeAroundHandlers('horizline', $parser, array($mType, $mod)); + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string + * @param TexyModifier + * @return TexyHtml + */ + public function solve($invocation, $type, $modifier) + { + $el = TexyHtml::el('hr'); + $modifier->decorate($invocation->texy, $el); + + $class = $this->classes[ $type[0] ]; + if ($class && !isset($modifier->classes[$class])) { + $el->attrs['class'][] = $class; + } + + return $el; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyHtmlModule.php b/apigen/libs/Texy/texy/modules/TexyHtmlModule.php new file mode 100644 index 00000000000..052cb8269dd --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyHtmlModule.php @@ -0,0 +1,281 @@ +texy = $texy; + + $texy->addHandler('htmlComment', array($this, 'solveComment')); + $texy->addHandler('htmlTag', array($this, 'solveTag')); + + $texy->registerLinePattern( + array($this, 'patternTag'), + '#<(/?)([a-z][a-z0-9_:-]*)((?:\s+[a-z0-9:-]+|=\s*"[^"'.TEXY_MARK.']*"|=\s*\'[^\''.TEXY_MARK.']*\'|=[^\s>'.TEXY_MARK.']+)*)\s*(/?)>#isu', + 'html/tag' + ); + + $texy->registerLinePattern( + array($this, 'patternComment'), + '##is', + 'html/comment' + ); + } + + + + /** + * Callback for: . + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternComment($parser, $matches) + { + list(, $mComment) = $matches; + return $this->texy->invokeAroundHandlers('htmlComment', $parser, array($mComment)); + } + + + + /** + * Callback for: . + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternTag($parser, $matches) + { + list(, $mEnd, $mTag, $mAttr, $mEmpty) = $matches; + // [1] => / + // [2] => tag + // [3] => attributes + // [4] => / + + $tx = $this->texy; + + $isStart = $mEnd !== '/'; + $isEmpty = $mEmpty === '/'; + if (!$isEmpty && substr($mAttr, -1) === '/') { // uvizlo v $mAttr? + $mAttr = substr($mAttr, 0, -1); + $isEmpty = TRUE; + } + + // error - can't close empty element + if ($isEmpty && !$isStart) + return FALSE; + + + // error - end element with atttrs + $mAttr = trim(strtr($mAttr, "\n", ' ')); + if ($mAttr && !$isStart) + return FALSE; + + + $el = TexyHtml::el($mTag); + + if ($isStart) { + // parse attributes + $matches2 = NULL; + preg_match_all( + '#([a-z0-9:-]+)\s*(?:=\s*(\'[^\']*\'|"[^"]*"|[^\'"\s]+))?()#isu', + $mAttr, + $matches2, + PREG_SET_ORDER + ); + + foreach ($matches2 as $m) { + $key = strtolower($m[1]); + $value = $m[2]; + if ($value == NULL) $el->attrs[$key] = TRUE; + elseif ($value{0} === '\'' || $value{0} === '"') $el->attrs[$key] = Texy::unescapeHtml(substr($value, 1, -1)); + else $el->attrs[$key] = Texy::unescapeHtml($value); + } + } + + $res = $tx->invokeAroundHandlers('htmlTag', $parser, array($el, $isStart, $isEmpty)); + + if ($res instanceof TexyHtml) { + return $tx->protect($isStart ? $res->startTag() : $res->endTag(), $res->getContentType()); + } + + return $res; + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param TexyHtml element + * @param bool is start tag? + * @param bool is empty? + * @return TexyHtml|string|FALSE + */ + public function solveTag($invocation, TexyHtml $el, $isStart, $forceEmpty = NULL) + { + $tx = $this->texy; + + // tag & attibutes + $allowedTags = $tx->allowedTags; // speed-up + if (!$allowedTags) + return FALSE; // all tags are disabled + + // convert case + $name = $el->getName(); + $lower = strtolower($name); + if (isset($tx->dtd[$lower]) || $name === strtoupper($name)) { + // complete UPPER convert to lower + $name = $lower; + $el->setName($name); + } + + if (is_array($allowedTags)) { + if (!isset($allowedTags[$name])) return FALSE; + $allowedAttrs = $allowedTags[$name]; // allowed attrs + + } else { + // allowedTags === Texy::ALL + if ($forceEmpty) $el->setName($name, TRUE); + $allowedAttrs = Texy::ALL; // all attrs are allowed + } + + // end tag? we are finished + if (!$isStart) { + return $el; + } + + $elAttrs = & $el->attrs; + + // process attributes + if (!$allowedAttrs) { + $elAttrs = array(); + + } elseif (is_array($allowedAttrs)) { + + // skip disabled + $allowedAttrs = array_flip($allowedAttrs); + foreach ($elAttrs as $key => $foo) + if (!isset($allowedAttrs[$key])) unset($elAttrs[$key]); + } + + // apply allowedClasses + $tmp = $tx->_classes; // speed-up + if (isset($elAttrs['class'])) { + if (is_array($tmp)) { + $elAttrs['class'] = explode(' ', $elAttrs['class']); + foreach ($elAttrs['class'] as $key => $value) + if (!isset($tmp[$value])) unset($elAttrs['class'][$key]); // id & class are case-sensitive + + } elseif ($tmp !== Texy::ALL) { + $elAttrs['class'] = NULL; + } + } + + // apply allowedClasses for ID + if (isset($elAttrs['id'])) { + if (is_array($tmp)) { + if (!isset($tmp['#' . $elAttrs['id']])) $elAttrs['id'] = NULL; + } elseif ($tmp !== Texy::ALL) { + $elAttrs['id'] = NULL; + } + } + + // apply allowedStyles + if (isset($elAttrs['style'])) { + $tmp = $tx->_styles; // speed-up + if (is_array($tmp)) { + $styles = explode(';', $elAttrs['style']); + $elAttrs['style'] = NULL; + foreach ($styles as $value) { + $pair = explode(':', $value, 2); + $prop = trim($pair[0]); + if (isset($pair[1]) && isset($tmp[strtolower($prop)])) // CSS is case-insensitive + $elAttrs['style'][$prop] = $pair[1]; + } + } elseif ($tmp !== Texy::ALL) { + $elAttrs['style'] = NULL; + } + } + + if ($name === 'img') { + if (!isset($elAttrs['src'])) return FALSE; + + if (!$tx->checkURL($elAttrs['src'], Texy::FILTER_IMAGE)) return FALSE; + + $tx->summary['images'][] = $elAttrs['src']; + + } elseif ($name === 'a') { + if (!isset($elAttrs['href']) && !isset($elAttrs['name']) && !isset($elAttrs['id'])) return FALSE; + if (isset($elAttrs['href'])) { + if ($tx->linkModule->forceNoFollow && strpos($elAttrs['href'], '//') !== FALSE) { + if (isset($elAttrs['rel'])) $elAttrs['rel'] = (array) $elAttrs['rel']; + $elAttrs['rel'][] = 'nofollow'; + } + + if (!$tx->checkURL($elAttrs['href'], Texy::FILTER_ANCHOR)) return FALSE; + + $tx->summary['links'][] = $elAttrs['href']; + } + + } elseif (preg_match('#^h[1-6]#i', $name)) { + $tx->headingModule->TOC[] = array( + 'el' => $el, + 'level' => (int) substr($name, 1), + 'type' => 'html', + ); + } + + $el->validateAttrs($tx->dtd); + + return $el; + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string + * @return string + */ + public function solveComment($invocation, $content) + { + if (!$this->passComment) return ''; + + // sanitize comment + $content = preg_replace('#-{2,}#', '-', $content); + $content = trim($content, '-'); + + return $this->texy->protect('', Texy::CONTENT_MARKUP); + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyHtmlOutputModule.php b/apigen/libs/Texy/texy/modules/TexyHtmlOutputModule.php new file mode 100644 index 00000000000..92ff55b77fd --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyHtmlOutputModule.php @@ -0,0 +1,311 @@ +texy = $texy; + $texy->addHandler('postProcess', array($this, 'postProcess')); + } + + + + /** + * Converts ... ... . + * into ... ... + */ + public function postProcess($texy, & $s) + { + $this->space = $this->baseIndent; + $this->tagStack = array(); + $this->tagUsed = array(); + $this->xml = $texy->getOutputMode() & Texy::XML; + + // special "base content" + $this->baseDTD = $texy->dtd['div'][1] + $texy->dtd['html'][1] /*+ $texy->dtd['head'][1]*/ + $texy->dtd['body'][1] + array('html'=>1); + + // wellform and reformat + $s = preg_replace_callback( + '#(.*)<(?:(!--.*--)|(/?)([a-z][a-z0-9._:-]*)(|[ \n].*)\s*(/?))>()#Uis', + array($this, 'cb'), + $s . '' + ); + + // empty out stack + foreach ($this->tagStack as $item) $s .= $item['close']; + + // right trim + $s = preg_replace("#[\t ]+(\n|\r|$)#", '$1', $s); // right trim + + // join double \r to single \n + $s = str_replace("\r\r", "\n", $s); + $s = strtr($s, "\r", "\n"); + + // greedy chars + $s = preg_replace("#\\x07 *#", '', $s); + // back-tabs + $s = preg_replace("#\\t? *\\x08#", '', $s); + + // line wrap + if ($this->lineWrap > 0) { + $s = preg_replace_callback( + '#^(\t*)(.*)$#m', + array($this, 'wrap'), + $s + ); + } + + // remove HTML 4.01 optional end tags + if (!$this->xml && $this->removeOptional) { + $s = preg_replace('#\\s*#u', '', $s); + } + } + + + + /** + * Callback function: | | .... + * @return string + */ + private function cb($matches) + { + // html tag + list(, $mText, $mComment, $mEnd, $mTag, $mAttr, $mEmpty) = $matches; + // [1] => text + // [1] => !-- comment -- + // [2] => / + // [3] => TAG + // [4] => ... (attributes) + // [5] => / (empty) + + $s = ''; + + // phase #1 - stuff between tags + if ($mText !== '') { + $item = reset($this->tagStack); + // text not allowed? + if ($item && !isset($item['dtdContent']['%DATA'])) { } + + // inside pre & textarea preserve spaces + elseif (!empty($this->tagUsed['pre']) || !empty($this->tagUsed['textarea']) || !empty($this->tagUsed['script'])) + $s = Texy::freezeSpaces($mText); + + // otherwise shrink multiple spaces + else $s = preg_replace('#[ \n]+#', ' ', $mText); + } + + + // phase #2 - HTML comment + if ($mComment) return $s . '<' . Texy::freezeSpaces($mComment) . '>'; + + + // phase #3 - HTML tag + $mEmpty = $mEmpty || isset(TexyHtml::$emptyElements[$mTag]); + if ($mEmpty && $mEnd) return $s; // bad tag; /end/ + + + if ($mEnd) { // end tag + + // has start tag? + if (empty($this->tagUsed[$mTag])) return $s; + + // autoclose tags + $tmp = array(); + $back = TRUE; + foreach ($this->tagStack as $i => $item) + { + $tag = $item['tag']; + $s .= $item['close']; + $this->space -= $item['indent']; + $this->tagUsed[$tag]--; + $back = $back && isset(TexyHtml::$inlineElements[$tag]); + unset($this->tagStack[$i]); + if ($tag === $mTag) break; + array_unshift($tmp, $item); + } + + if (!$back || !$tmp) return $s; + + // allowed-check (nejspis neni ani potreba) + $item = reset($this->tagStack); + if ($item) $dtdContent = $item['dtdContent']; + else $dtdContent = $this->baseDTD; + if (!isset($dtdContent[$tmp[0]['tag']])) return $s; + + // autoopen tags + foreach ($tmp as $item) + { + $s .= $item['open']; + $this->space += $item['indent']; + $this->tagUsed[$item['tag']]++; + array_unshift($this->tagStack, $item); + } + + + } else { // start tag + + $dtdContent = $this->baseDTD; + + if (!isset($this->texy->dtd[$mTag])) { + // unknown (non-html) tag + $allowed = TRUE; + $item = reset($this->tagStack); + if ($item) $dtdContent = $item['dtdContent']; + + + } else { + // optional end tag closing + foreach ($this->tagStack as $i => $item) + { + // is tag allowed here? + $dtdContent = $item['dtdContent']; + if (isset($dtdContent[$mTag])) break; + + $tag = $item['tag']; + + // auto-close hidden, optional and inline tags + if ($item['close'] && (!isset(TexyHtml::$optionalEnds[$tag]) && !isset(TexyHtml::$inlineElements[$tag]))) break; + + // close it + $s .= $item['close']; + $this->space -= $item['indent']; + $this->tagUsed[$tag]--; + unset($this->tagStack[$i]); + $dtdContent = $this->baseDTD; + } + + // is tag allowed in this content? + $allowed = isset($dtdContent[$mTag]); + + // check deep element prohibitions + if ($allowed && isset(TexyHtml::$prohibits[$mTag])) { + foreach (TexyHtml::$prohibits[$mTag] as $pTag) + if (!empty($this->tagUsed[$pTag])) { $allowed = FALSE; break; } + } + } + + // empty elements se neukladaji do zasobniku + if ($mEmpty) { + if (!$allowed) return $s; + + if ($this->xml) $mAttr .= " /"; + + $indent = $this->indent && empty($this->tagUsed['pre']) && empty($this->tagUsed['textarea']); + + if ($indent && $mTag === 'br') + // formatting exception + return rtrim($s) . '<' . $mTag . $mAttr . ">\n" . str_repeat("\t", max(0, $this->space - 1)) . "\x07"; + + if ($indent && !isset(TexyHtml::$inlineElements[$mTag])) { + $space = "\r" . str_repeat("\t", $this->space); + return $s . $space . '<' . $mTag . $mAttr . '>' . $space; + } + + return $s . '<' . $mTag . $mAttr . '>'; + } + + $open = NULL; + $close = NULL; + $indent = 0; + + /* + if (!isset(TexyHtml::$inlineElements[$mTag])) { + // block tags always decorate with \n + $s .= "\n"; + $close = "\n"; + } + */ + + if ($allowed) { + $open = '<' . $mTag . $mAttr . '>'; + + // receive new content (ins & del are special cases) + if (!empty($this->texy->dtd[$mTag][1])) $dtdContent = $this->texy->dtd[$mTag][1]; + + // format output + if ($this->indent && !isset(TexyHtml::$inlineElements[$mTag])) { + $close = "\x08" . '' . "\n" . str_repeat("\t", $this->space); + $s .= "\n" . str_repeat("\t", $this->space++) . $open . "\x07"; + $indent = 1; + } else { + $close = ''; + $s .= $open; + } + + // TODO: problematic formatting of select / options, object / params + } + + + // open tag, put to stack, increase counter + $item = array( + 'tag' => $mTag, + 'open' => $open, + 'close' => $close, + 'dtdContent' => $dtdContent, + 'indent' => $indent, + ); + array_unshift($this->tagStack, $item); + $tmp = &$this->tagUsed[$mTag]; $tmp++; + } + + return $s; + } + + + + /** + * Callback function: wrap lines. + * @return string + */ + private function wrap($m) + { + list(, $space, $s) = $m; + return $space . wordwrap($s, $this->lineWrap, "\n" . $space); + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyImageModule.php b/apigen/libs/Texy/texy/modules/TexyImageModule.php new file mode 100644 index 00000000000..ffd7acbc75e --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyImageModule.php @@ -0,0 +1,372 @@ +texy = $texy; + + $texy->allowed['image/definition'] = TRUE; + $texy->addHandler('image', array($this, 'solve')); + $texy->addHandler('beforeParse', array($this, 'beforeParse')); + + // [*image*]:LINK + $texy->registerLinePattern( + array($this, 'patternImage'), + '#'.TEXY_IMAGE.TEXY_LINK_N.'??()#Uu', + 'image' + ); + } + + + + /** + * Text pre-processing. + * @param Texy + * @param string + * @return void + */ + public function beforeParse($texy, & $text) + { + if (!empty($texy->allowed['image/definition'])) { + // [*image*]: urls .(title)[class]{style} + $text = preg_replace_callback( + '#^\[\*([^\n]+)\*\]:\ +(.+)\ *'.TEXY_MODIFIER.'?\s*()$#mUu', + array($this, 'patternReferenceDef'), + $text + ); + } + } + + + + /** + * Callback for: [*image*]: urls .(title)[class]{style}. + * + * @param array regexp matches + * @return string + */ + private function patternReferenceDef($matches) + { + list(, $mRef, $mURLs, $mMod) = $matches; + // [1] => [* (reference) *] + // [2] => urls + // [3] => .(title)[class]{style}<> + + $image = $this->factoryImage($mURLs, $mMod, FALSE); + $this->addReference($mRef, $image); + return ''; + } + + + + /** + * Callback for [* small.jpg 80x13 | small-over.jpg | big.jpg .(alternative text)[class]{style}>]:LINK. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternImage($parser, $matches) + { + list(, $mURLs, $mMod, $mAlign, $mLink) = $matches; + // [1] => URLs + // [2] => .(title)[class]{style}<> + // [3] => * < > + // [4] => url | [ref] | [*image*] + + $tx = $this->texy; + + $image = $this->factoryImage($mURLs, $mMod.$mAlign); + + if ($mLink) { + if ($mLink === ':') { + $link = new TexyLink($image->linkedURL === NULL ? $image->URL : $image->linkedURL); + $link->raw = ':'; + $link->type = TexyLink::IMAGE; + } else { + $link = $tx->linkModule->factoryLink($mLink, NULL, NULL); + } + } else $link = NULL; + + return $tx->invokeAroundHandlers('image', $parser, array($image, $link)); + } + + + + /** + * Adds new named reference to image. + * + * @param string reference name + * @param TexyImage + * @return void + */ + public function addReference($name, TexyImage $image) + { + $image->name = TexyUtf::strtolower($name); + $this->references[$image->name] = $image; + } + + + + /** + * Returns named reference. + * + * @param string reference name + * @return TexyImage reference descriptor (or FALSE) + */ + public function getReference($name) + { + $name = TexyUtf::strtolower($name); + if (isset($this->references[$name])) + return clone $this->references[$name]; + + return FALSE; + } + + + + /** + * Parses image's syntax. + * @param string input: small.jpg 80x13 | small-over.jpg | linked.jpg + * @param string + * @param bool + * @return TexyImage + */ + public function factoryImage($content, $mod, $tryRef = TRUE) + { + $image = $tryRef ? $this->getReference(trim($content)) : FALSE; + + if (!$image) { + $tx = $this->texy; + $content = explode('|', $content); + $image = new TexyImage; + + // dimensions + $matches = NULL; + if (preg_match('#^(.*) (\d+|\?) *(X|x) *(\d+|\?) *()$#U', $content[0], $matches)) { + $image->URL = trim($matches[1]); + $image->asMax = $matches[3] === 'X'; + $image->width = $matches[2] === '?' ? NULL : (int) $matches[2]; + $image->height = $matches[4] === '?' ? NULL : (int) $matches[4]; + } else { + $image->URL = trim($content[0]); + } + + if (!$tx->checkURL($image->URL, Texy::FILTER_IMAGE)) $image->URL = NULL; + + // onmouseover image + if (isset($content[1])) { + $tmp = trim($content[1]); + if ($tmp !== '' && $tx->checkURL($tmp, Texy::FILTER_IMAGE)) $image->overURL = $tmp; + } + + // linked image + if (isset($content[2])) { + $tmp = trim($content[2]); + if ($tmp !== '' && $tx->checkURL($tmp, Texy::FILTER_ANCHOR)) $image->linkedURL = $tmp; + } + } + + $image->modifier->setProperties($mod); + return $image; + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param TexyImage + * @param TexyLink + * @return TexyHtml|FALSE + */ + public function solve($invocation, TexyImage $image, $link) + { + if ($image->URL == NULL) return FALSE; + + $tx = $this->texy; + + $mod = $image->modifier; + $alt = $mod->title; + $mod->title = NULL; + $hAlign = $mod->hAlign; + $mod->hAlign = NULL; + + $el = TexyHtml::el('img'); + $el->attrs['src'] = NULL; // trick - move to front + $mod->decorate($tx, $el); + $el->attrs['src'] = Texy::prependRoot($image->URL, $this->root); + if (!isset($el->attrs['alt'])) { + if ($alt !== NULL) $el->attrs['alt'] = $tx->typographyModule->postLine($alt); + else $el->attrs['alt'] = $this->defaultAlt; + } + + if ($hAlign) { + $var = $hAlign . 'Class'; // leftClass, rightClass + if (!empty($this->$var)) { + $el->attrs['class'][] = $this->$var; + + } elseif (empty($tx->alignClasses[$hAlign])) { + $el->attrs['style']['float'] = $hAlign; + + } else { + $el->attrs['class'][] = $tx->alignClasses[$hAlign]; + } + } + + if (!is_int($image->width) || !is_int($image->height) || $image->asMax) { + // autodetect fileRoot + if ($this->fileRoot === NULL && isset($_SERVER['SCRIPT_FILENAME'])) { + $this->fileRoot = dirname($_SERVER['SCRIPT_FILENAME']) . '/' . $this->root; + } + + // detect dimensions + // absolute URL & security check for double dot + if (Texy::isRelative($image->URL) && strpos($image->URL, '..') === FALSE) { + $file = rtrim($this->fileRoot, '/\\') . '/' . $image->URL; + if (@is_file($file)) { // intentionally @ + $size = @getImageSize($file); // intentionally @ + if (is_array($size)) { + if ($image->asMax) { + $ratio = 1; + if (is_int($image->width)) $ratio = min($ratio, $image->width / $size[0]); + if (is_int($image->height)) $ratio = min($ratio, $image->height / $size[1]); + $image->width = round($ratio * $size[0]); + $image->height = round($ratio * $size[1]); + + } elseif (is_int($image->width)) { + $ratio = round($size[1] / $size[0] * $image->width); + $image->height = round($size[1] / $size[0] * $image->width); + + } elseif (is_int($image->height)) { + $image->width = round($size[0] / $size[1] * $image->height); + + } else { + $image->width = $size[0]; + $image->height = $size[1]; + } + } + } + } + } + + $el->attrs['width'] = $image->width; + $el->attrs['height'] = $image->height; + + // onmouseover actions generate + if ($image->overURL !== NULL) { + $overSrc = Texy::prependRoot($image->overURL, $this->root); + $el->attrs['onmouseover'] = 'this.src=\'' . addSlashes($overSrc) . '\''; + $el->attrs['onmouseout'] = 'this.src=\'' . addSlashes($el->attrs['src']) . '\''; + $el->attrs['onload'] = str_replace('%i', addSlashes($overSrc), $this->onLoad); + $tx->summary['preload'][] = $overSrc; + } + + $tx->summary['images'][] = $el->attrs['src']; + + if ($link) return $tx->linkModule->solve(NULL, $link, $el); + + return $el; + } + +} + + + + + + + +/** + * @package Texy + */ +final class TexyImage extends TexyObject +{ + /** @var string base image URL */ + public $URL; + + /** @var string on-mouse-over image URL */ + public $overURL; + + /** @var string anchored image URL */ + public $linkedURL; + + /** @var int optional image width */ + public $width; + + /** @var int optional image height */ + public $height; + + /** @var bool image width and height are maximal */ + public $asMax; + + /** @var TexyModifier */ + public $modifier; + + /** @var string reference name (if is stored as reference) */ + public $name; + + + + public function __construct() + { + $this->modifier = new TexyModifier; + } + + + + public function __clone() + { + if ($this->modifier) { + $this->modifier = clone $this->modifier; + } + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyLinkModule.php b/apigen/libs/Texy/texy/modules/TexyLinkModule.php new file mode 100644 index 00000000000..16352282d8d --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyLinkModule.php @@ -0,0 +1,493 @@ +texy = $texy; + + $texy->allowed['link/definition'] = TRUE; + $texy->addHandler('newReference', array($this, 'solveNewReference')); + $texy->addHandler('linkReference', array($this, 'solve')); + $texy->addHandler('linkEmail', array($this, 'solveUrlEmail')); + $texy->addHandler('linkURL', array($this, 'solveUrlEmail')); + $texy->addHandler('beforeParse', array($this, 'beforeParse')); + + // [reference] + $texy->registerLinePattern( + array($this, 'patternReference'), + '#(\[[^\[\]\*\n'.TEXY_MARK.']+\])#U', + 'link/reference' + ); + + // direct url and email + $texy->registerLinePattern( + array($this, 'patternUrlEmail'), + '#(?<=^|[\s([<:\x17])(?:https?://|www\.|ftp://)[0-9.'.TEXY_CHAR.'-][/\d'.TEXY_CHAR.'+\.~%&?@=_:;\#,\x{ad}-]+[/\d'.TEXY_CHAR.'+~%?@=_\#]#u', + 'link/url', + '#(?:https?://|www\.|ftp://)#u' + ); + + $texy->registerLinePattern( + array($this, 'patternUrlEmail'), + '#(?<=^|[\s([<:\x17])'.TEXY_EMAIL.'#u', + 'link/email', + '#'.TEXY_EMAIL.'#u' + ); + } + + + + /** + * Text pre-processing. + * @param Texy + * @param string + * @return void + */ + public function beforeParse($texy, & $text) + { + self::$livelock = array(); + + // [la trine]: http://www.latrine.cz/ text odkazu .(title)[class]{style} + if (!empty($texy->allowed['link/definition'])) { + $text = preg_replace_callback( + '#^\[([^\[\]\#\?\*\n]+)\]: +(\S+)(\ .+)?'.TEXY_MODIFIER.'?\s*()$#mUu', + array($this, 'patternReferenceDef'), + $text + ); + } + } + + + + /** + * Callback for: [la trine]: http://www.latrine.cz/ text odkazu .(title)[class]{style}. + * + * @param array regexp matches + * @return string + */ + private function patternReferenceDef($matches) + { + list(, $mRef, $mLink, $mLabel, $mMod) = $matches; + // [1] => [ (reference) ] + // [2] => link + // [3] => ... + // [4] => .(title)[class]{style} + + $link = new TexyLink($mLink); + $link->label = trim($mLabel); + $link->modifier->setProperties($mMod); + $this->checkLink($link); + $this->addReference($mRef, $link); + return ''; + } + + + + /** + * Callback for: [ref]. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternReference($parser, $matches) + { + list(, $mRef) = $matches; + // [1] => [ref] + + $tx = $this->texy; + $name = substr($mRef, 1, -1); + $link = $this->getReference($name); + + if (!$link) { + return $tx->invokeAroundHandlers('newReference', $parser, array($name)); + } + + $link->type = TexyLink::BRACKET; + + if ($link->label != '') { // NULL or '' + // prevent circular references + if (isset(self::$livelock[$link->name])) { + $content = $link->label; + } else { + self::$livelock[$link->name] = TRUE; + $el = TexyHtml::el(); + $lineParser = new TexyLineParser($tx, $el); + $lineParser->parse($link->label); + $content = $el->toString($tx); + unset(self::$livelock[$link->name]); + } + } else { + $content = $this->textualUrl($link); + $content = $this->texy->protect($content, Texy::CONTENT_TEXTUAL); + } + + return $tx->invokeAroundHandlers('linkReference', $parser, array($link, $content)); + } + + + + /** + * Callback for: http://davidgrudl.com david@grudl.com. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternUrlEmail($parser, $matches, $name) + { + list($mURL) = $matches; + // [0] => URL + + $link = new TexyLink($mURL); + $this->checkLink($link); + + return $this->texy->invokeAroundHandlers( + $name === 'link/email' ? 'linkEmail' : 'linkURL', + $parser, + array($link) + ); + } + + + + /** + * Adds new named reference. + * + * @param string reference name + * @param TexyLink + * @return void + */ + public function addReference($name, TexyLink $link) + { + $link->name = TexyUtf::strtolower($name); + $this->references[$link->name] = $link; + } + + + + /** + * Returns named reference. + * + * @param string reference name + * @return TexyLink reference descriptor (or FALSE) + */ + public function getReference($name) + { + $name = TexyUtf::strtolower($name); + if (isset($this->references[$name])) { + return clone $this->references[$name]; + + } else { + $pos = strpos($name, '?'); + if ($pos === FALSE) $pos = strpos($name, '#'); + if ($pos !== FALSE) { // try to extract ?... #... part + $name2 = substr($name, 0, $pos); + if (isset($this->references[$name2])) { + $link = clone $this->references[$name2]; + $link->URL .= substr($name, $pos); + return $link; + } + } + } + + return FALSE; + } + + + + /** + * @param string + * @param string + * @param string + * @return TexyLink + */ + public function factoryLink($dest, $mMod, $label) + { + $tx = $this->texy; + $type = TexyLink::COMMON; + + // [ref] + if (strlen($dest)>1 && $dest{0} === '[' && $dest{1} !== '*') { + $type = TexyLink::BRACKET; + $dest = substr($dest, 1, -1); + $link = $this->getReference($dest); + + // [* image *] + } elseif (strlen($dest)>1 && $dest{0} === '[' && $dest{1} === '*') { + $type = TexyLink::IMAGE; + $dest = trim(substr($dest, 2, -2)); + $image = $tx->imageModule->getReference($dest); + if ($image) { + $link = new TexyLink($image->linkedURL === NULL ? $image->URL : $image->linkedURL); + $link->modifier = $image->modifier; + } + } + + if (empty($link)) { + $link = new TexyLink(trim($dest)); + $this->checkLink($link); + } + + if (strpos($link->URL, '%s') !== FALSE) { + $link->URL = str_replace('%s', urlencode($tx->stringToText($label)), $link->URL); + } + $link->modifier->setProperties($mMod); + $link->type = $type; + return $link; + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param TexyLink + * @param TexyHtml|string + * @return TexyHtml|string + */ + public function solve($invocation, $link, $content = NULL) + { + if ($link->URL == NULL) return $content; + + $tx = $this->texy; + + $el = TexyHtml::el('a'); + + if (empty($link->modifier)) { + $nofollow = $popup = FALSE; + } else { + $nofollow = isset($link->modifier->classes['nofollow']); + $popup = isset($link->modifier->classes['popup']); + unset($link->modifier->classes['nofollow'], $link->modifier->classes['popup']); + $el->attrs['href'] = NULL; // trick - move to front + $link->modifier->decorate($tx, $el); + } + + if ($link->type === TexyLink::IMAGE) { + // image + $el->attrs['href'] = Texy::prependRoot($link->URL, $tx->imageModule->linkedRoot); + $el->attrs['onclick'] = $this->imageOnClick; + + } else { + $el->attrs['href'] = Texy::prependRoot($link->URL, $this->root); + + // rel="nofollow" + if ($nofollow || ($this->forceNoFollow && strpos($el->attrs['href'], '//') !== FALSE)) + $el->attrs['rel'] = 'nofollow'; + } + + // popup on click + if ($popup) $el->attrs['onclick'] = $this->popupOnClick; + + if ($content !== NULL) $el->add($content); + + $tx->summary['links'][] = $el->attrs['href']; + + return $el; + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param TexyLink + * @return TexyHtml|string + */ + public function solveUrlEmail($invocation, $link) + { + $content = $this->textualUrl($link); + $content = $this->texy->protect($content, Texy::CONTENT_TEXTUAL); + return $this->solve(NULL, $link, $content); + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string + * @return FALSE + */ + public function solveNewReference($invocation, $name) + { + // no change + return FALSE; + } + + + + /** + * Checks and corrects $URL. + * @param TexyLink + * @return void + */ + private function checkLink($link) + { + // remove soft hyphens; if not removed by Texy::process() + $link->URL = str_replace("\xC2\xAD", '', $link->URL); + + if (strncasecmp($link->URL, 'www.', 4) === 0) { + // special supported case + $link->URL = 'http://' . $link->URL; + + } elseif (preg_match('#'.TEXY_EMAIL.'$#Au', $link->URL)) { + // email + $link->URL = 'mailto:' . $link->URL; + + } elseif (!$this->texy->checkURL($link->URL, Texy::FILTER_ANCHOR)) { + $link->URL = NULL; + + } else { + $link->URL = str_replace('&', '&', $link->URL); // replace unwanted & + } + } + + + + /** + * Returns textual representation of URL. + * @param TexyLink + * @return string + */ + private function textualUrl($link) + { + if ($this->texy->obfuscateEmail && preg_match('#^'.TEXY_EMAIL.'$#u', $link->raw)) { // email + return str_replace('@', "@", $link->raw); + } + + if ($this->shorten && preg_match('#^(https?://|ftp://|www\.|/)#i', $link->raw)) { + + $raw = strncasecmp($link->raw, 'www.', 4) === 0 ? 'none://' . $link->raw : $link->raw; + + // parse_url() in PHP damages UTF-8 - use regular expression + if (!preg_match('~^(?:(?P[a-z]+):)?(?://(?P[^/?#]+))?(?P(?:/|^)(?!/)[^?#]*)?(?:\?(?P[^#]*))?(?:#(?P.*))?()$~', $raw, $parts)) { + return $link->raw; + } + + $res = ''; + if ($parts['scheme'] !== '' && $parts['scheme'] !== 'none') + $res .= $parts['scheme'] . '://'; + + if ($parts['host'] !== '') + $res .= $parts['host']; + + if ($parts['path'] !== '') + $res .= (iconv_strlen($parts['path'], 'UTF-8') > 16 ? ("/\xe2\x80\xa6" . iconv_substr($parts['path'], -12, 12, 'UTF-8')) : $parts['path']); + + if ($parts['query'] !== '') { + $res .= iconv_strlen($parts['query'], 'UTF-8') > 4 ? "?\xe2\x80\xa6" : ('?' . $parts['query']); + } elseif ($parts['fragment'] !== '') { + $res .= iconv_strlen($parts['fragment'], 'UTF-8') > 4 ? "#\xe2\x80\xa6" : ('#' . $parts['fragment']); + } + return $res; + } + + return $link->raw; + } + +} + + + + + + + + + +/** + * @package Texy + */ +final class TexyLink extends TexyObject +{ + /** @see $type */ + const + COMMON = 1, + BRACKET = 2, + IMAGE = 3; + + /** @var string URL in resolved form */ + public $URL; + + /** @var string URL as written in text */ + public $raw; + + /** @var TexyModifier */ + public $modifier; + + /** @var int how was link created? */ + public $type = TexyLink::COMMON; + + /** @var string optional label, used by references */ + public $label; + + /** @var string reference name (if is stored as reference) */ + public $name; + + + + public function __construct($URL) + { + $this->URL = $URL; + $this->raw = $URL; + $this->modifier = new TexyModifier; + } + + + + public function __clone() + { + if ($this->modifier) { + $this->modifier = clone $this->modifier; + } + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyListModule.php b/apigen/libs/Texy/texy/modules/TexyListModule.php new file mode 100644 index 00000000000..11ff52e25ba --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyListModule.php @@ -0,0 +1,255 @@ + array('\*\ ', 0, ''), + '-' => array('[\x{2013}-](?![>-])',0, ''), + '+' => array('\+\ ', 0, ''), + '1.' => array('1\.\ ',/* not \d !*/ 1, '', '\d{1,3}\.\ '), + '1)' => array('\d{1,3}\)\ ', 1, ''), + 'I.' => array('I\.\ ', 1, 'upper-roman', '[IVX]{1,4}\.\ '), + 'I)' => array('[IVX]+\)\ ', 1, 'upper-roman'), // before A) ! + 'a)' => array('[a-z]\)\ ', 1, 'lower-alpha'), + 'A)' => array('[A-Z]\)\ ', 1, 'upper-alpha'), + ); + + + + public function __construct($texy) + { + $this->texy = $texy; + + $texy->addHandler('beforeParse', array($this, 'beforeParse')); + $texy->allowed['list'] = TRUE; + $texy->allowed['list/definition'] = TRUE; + } + + + + public function beforeParse() + { + $RE = $REul = array(); + foreach ($this->bullets as $desc) { + $RE[] = $desc[0]; + if (!$desc[1]) $REul[] = $desc[0]; + } + + $this->texy->registerBlockPattern( + array($this, 'patternList'), + '#^(?:'.TEXY_MODIFIER_H.'\n)?' // .{color: red} + . '('.implode('|', $RE).')\ *\S.*$#mUu', // item (unmatched) + 'list' + ); + + $this->texy->registerBlockPattern( + array($this, 'patternDefList'), + '#^(?:'.TEXY_MODIFIER_H.'\n)?' // .{color:red} + . '(\S.*)\:\ *'.TEXY_MODIFIER_H.'?\n' // Term: + . '(\ ++)('.implode('|', $REul).')\ *\S.*$#mUu', // - description + 'list/definition' + ); + } + + + + /** + * Callback for:. + * + * 1) .... .(title)[class]{style}> + * 2) .... + * + ... + * + ... + * 3) .... + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|FALSE + */ + public function patternList($parser, $matches) + { + list(, $mMod, $mBullet) = $matches; + // [1] => .(title)[class]{style}<> + // [2] => bullet * + - 1) a) A) IV) + + $tx = $this->texy; + + $el = TexyHtml::el(); + + $bullet = $min = NULL; + foreach ($this->bullets as $type => $desc) + if (preg_match('#'.$desc[0].'#Au', $mBullet)) { + $bullet = isset($desc[3]) ? $desc[3] : $desc[0]; + $min = isset($desc[3]) ? 2 : 1; + $el->setName($desc[1] ? 'ol' : 'ul'); + $el->attrs['style']['list-style-type'] = $desc[2]; + if ($desc[1]) { // ol + if ($type[0] === '1' && (int) $mBullet > 1) + $el->attrs['start'] = (int) $mBullet; + elseif ($type[0] === 'a' && $mBullet[0] > 'a') + $el->attrs['start'] = ord($mBullet[0]) - 96; + elseif ($type[0] === 'A' && $mBullet[0] > 'A') + $el->attrs['start'] = ord($mBullet[0]) - 64; + } + break; + } + + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $el); + + $parser->moveBackward(1); + + while ($elItem = $this->patternItem($parser, $bullet, FALSE, 'li')) { + $el->add($elItem); + } + + if ($el->count() < $min) return FALSE; + + // event listener + $tx->invokeHandlers('afterList', array($parser, $el, $mod)); + + return $el; + } + + + + /** + * Callback for:. + * + * Term: .(title)[class]{style}> + * - description 1 + * - description 2 + * - description 3 + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml + */ + public function patternDefList($parser, $matches) + { + list(, $mMod, , , , $mBullet) = $matches; + // [1] => .(title)[class]{style}<> + // [2] => ... + // [3] => .(title)[class]{style}<> + // [4] => space + // [5] => - * + + + $tx = $this->texy; + + $bullet = NULL; + foreach ($this->bullets as $desc) + if (preg_match('#'.$desc[0].'#Au', $mBullet)) { + $bullet = isset($desc[3]) ? $desc[3] : $desc[0]; + break; + } + + $el = TexyHtml::el('dl'); + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $el); + $parser->moveBackward(2); + + $patternTerm = '#^\n?(\S.*)\:\ *'.TEXY_MODIFIER_H.'?()$#mUA'; + + while (TRUE) { + if ($elItem = $this->patternItem($parser, $bullet, TRUE, 'dd')) { + $el->add($elItem); + continue; + } + + if ($parser->next($patternTerm, $matches)) { + list(, $mContent, $mMod) = $matches; + // [1] => ... + // [2] => .(title)[class]{style}<> + + $elItem = TexyHtml::el('dt'); + $modItem = new TexyModifier($mMod); + $modItem->decorate($tx, $elItem); + + $elItem->parseLine($tx, $mContent); + $el->add($elItem); + continue; + } + + break; + } + + // event listener + $tx->invokeHandlers('afterDefinitionList', array($parser, $el, $mod)); + + return $el; + } + + + + /** + * Callback for single list item. + * + * @param TexyBlockParser + * @param string bullet type + * @param string left space + * @param string html tag + * @return TexyHtml|FALSE + */ + public function patternItem($parser, $bullet, $indented, $tag) + { + $tx = $this->texy; + $spacesBase = $indented ? ('\ {1,}') : ''; + $patternItem = "#^\n?($spacesBase)$bullet\\ *(\\S.*)?".TEXY_MODIFIER_H."?()$#mAUu"; + + // first line with bullet + $matches = NULL; + if (!$parser->next($patternItem, $matches)) return FALSE; + + list(, $mIndent, $mContent, $mMod) = $matches; + // [1] => indent + // [2] => ... + // [3] => .(title)[class]{style}<> + + $elItem = TexyHtml::el($tag); + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $elItem); + + // next lines + $spaces = ''; + $content = ' ' . $mContent; // trick + while ($parser->next('#^(\n*)'.$mIndent.'(\ {1,'.$spaces.'})(.*)()$#Am', $matches)) { + list(, $mBlank, $mSpaces, $mContent) = $matches; + // [1] => blank line? + // [2] => spaces + // [3] => ... + + if ($spaces === '') $spaces = strlen($mSpaces); + $content .= "\n" . $mBlank . $mContent; + } + + // parse content + $elItem->parseBlock($tx, $content, TRUE); + + if (isset($elItem[0]) && $elItem[0] instanceof TexyHtml) { + $elItem[0]->setName(NULL); + } + + return $elItem; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyLongWordsModule.php b/apigen/libs/Texy/texy/modules/TexyLongWordsModule.php new file mode 100644 index 00000000000..16c1336009c --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyLongWordsModule.php @@ -0,0 +1,201 @@ +texy = $texy; + + $this->consonants = array_flip($this->consonants); + $this->vowels = array_flip($this->vowels); + $this->before_r = array_flip($this->before_r); + $this->before_l = array_flip($this->before_l); + $this->before_h = array_flip($this->before_h); + $this->doubleVowels = array_flip($this->doubleVowels); + + $texy->registerPostLine(array($this, 'postLine'), 'longwords'); + } + + + + public function postLine($text) + { + return preg_replace_callback( + '#[^\ \n\t\x14\x15\x16\x{2013}\x{2014}\x{ad}-]{'.$this->wordLimit.',}#u', + array($this, 'pattern'), + $text + ); + } + + + + /** + * Callback for long words. + * (c) David Grudl + * @param array + * @return string + */ + private function pattern($matches) + { + list($mWord) = $matches; + // [0] => lllloooonnnnggggwwwoorrdddd + + $chars = array(); + preg_match_all( + '#['.TEXY_MARK.']+|.#u', + $mWord, + $chars + ); + + $chars = $chars[0]; + if (count($chars) < $this->wordLimit) return $mWord; + + $consonants = $this->consonants; + $vowels = $this->vowels; + $before_r = $this->before_r; + $before_l = $this->before_l; + $before_h = $this->before_h; + $doubleVowels = $this->doubleVowels; + + $s = array(); + $trans = array(); + + $s[] = ''; + $trans[] = -1; + foreach ($chars as $key => $char) { + if (ord($char{0}) < 32) continue; + $s[] = $char; + $trans[] = $key; + } + $s[] = ''; + $len = count($s) - 2; + + $positions = array(); + $a = 0; $last = 1; + + while (++$a < $len) { + $hyphen = self::DONT; // Do not hyphenate + do { + if ($s[$a] === "\xC2\xA0") { $a++; continue 2; } // here and after never + + if ($s[$a] === '.') { $hyphen = self::HERE; break; } + + if (isset($consonants[$s[$a]])) { // consonants + + if (isset($vowels[$s[$a+1]])) { + if (isset($vowels[$s[$a-1]])) $hyphen = self::HERE; + break; + } + + if (($s[$a] === 's') && ($s[$a-1] === 'n') && isset($consonants[$s[$a+1]])) { $hyphen = self::AFTER; break; } + + if (isset($consonants[$s[$a+1]]) && isset($vowels[$s[$a-1]])) { + if ($s[$a+1] === 'r') { + $hyphen = isset($before_r[$s[$a]]) ? self::HERE : self::AFTER; + break; + } + + if ($s[$a+1] === 'l') { + $hyphen = isset($before_l[$s[$a]]) ? self::HERE : self::AFTER; + break; + } + + if ($s[$a+1] === 'h') { // CH + $hyphen = isset($before_h[$s[$a]]) ? self::DONT : self::AFTER; + break; + } + + $hyphen = self::AFTER; + break; + } + + break; + } // end of consonants + + if (($s[$a] === 'u') && isset($doubleVowels[$s[$a-1]])) { $hyphen = self::AFTER; break; } + if (isset($vowels[$s[$a]]) && isset($vowels[$s[$a-1]])) { $hyphen = self::HERE; break; } + + } while(0); + + if ($hyphen === self::DONT && ($a - $last > $this->wordLimit*0.6)) $positions[] = $last = $a-1; // Hyphenate here + if ($hyphen === self::HERE) $positions[] = $last = $a-1; // Hyphenate here + if ($hyphen === self::AFTER) { $positions[] = $last = $a; $a++; } // Hyphenate after + + } // while + + + $a = end($positions); + if (($a === $len-1) && isset($consonants[$s[$len]])) + array_pop($positions); + + + $syllables = array(); + $last = 0; + foreach ($positions as $pos) { + if ($pos - $last > $this->wordLimit*0.6) { + $syllables[] = implode('', array_splice($chars, 0, $trans[$pos] - $trans[$last])); + $last = $pos; + } + } + $syllables[] = implode('', $chars); + + //$s = implode("\xC2\xAD", $syllables); // insert shy + //$s = str_replace(array("\xC2\xAD\xC2\xA0", "\xC2\xA0\xC2\xAD"), array(' ', ' '), $s); // shy+nbsp = normal space + + return implode("\xC2\xAD", $syllables);; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyParagraphModule.php b/apigen/libs/Texy/texy/modules/TexyParagraphModule.php new file mode 100644 index 00000000000..5d8644b30a2 --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyParagraphModule.php @@ -0,0 +1,136 @@ +texy = $texy; + $texy->addHandler('paragraph', array($this, 'solve')); + } + + + + /** + * @param TexyBlockParser + * @param string text + * @param array + * @param TexyHtml + * @return vois + */ + public function process($parser, $content, $el) + { + $tx = $this->texy; + + if ($parser->isIndented()) { + $parts = preg_split('#(\n(?! )|\n{2,})#', $content, -1, PREG_SPLIT_NO_EMPTY); + } else { + $parts = preg_split('#(\n{2,})#', $content, -1, PREG_SPLIT_NO_EMPTY); + } + + foreach ($parts as $s) + { + $s = trim($s); + if ($s === '') continue; + + // try to find modifier + $mx = $mod = NULL; + if (preg_match('#\A(.*)(?<=\A|\S)'.TEXY_MODIFIER_H.'(\n.*)?()\z#sUm', $s, $mx)) { + list(, $mC1, $mMod, $mC2) = $mx; + $s = trim($mC1 . $mC2); + if ($s === '') continue; + $mod = new TexyModifier; + $mod->setProperties($mMod); + } + + $res = $tx->invokeAroundHandlers('paragraph', $parser, array($s, $mod)); + if ($res) $el->insert(NULL, $res); + } + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string + * @param TexyModifier|NULL + * @return TexyHtml|FALSE + */ + public function solve($invocation, $content, $mod) + { + $tx = $this->texy; + + // find hard linebreaks + if ($tx->mergeLines) { + // .... + // ... => \r means break line + $content = preg_replace('#\n +(?=\S)#', "\r", $content); + } else { + $content = preg_replace('#\n#', "\r", $content); + } + + $el = TexyHtml::el('p'); + $el->parseLine($tx, $content); + $content = $el->getText(); // string + + // check content type + // block contains block tag + if (strpos($content, Texy::CONTENT_BLOCK) !== FALSE) { + $el->setName(NULL); // ignores modifier! + + // block contains text (protected) + } elseif (strpos($content, Texy::CONTENT_TEXTUAL) !== FALSE) { + // leave element p + + // block contains text + } elseif (preg_match('#[^\s'.TEXY_MARK.']#u', $content)) { + // leave element p + + // block contains only replaced element + } elseif (strpos($content, Texy::CONTENT_REPLACED) !== FALSE) { + $el->setName($tx->nontextParagraph); + + // block contains only markup tags or spaces or nothing + } else { + // if {ignoreEmptyStuff} return FALSE; + if (!$mod) $el->setName(NULL); + } + + if ($el->getName()) { + // apply modifier + if ($mod) $mod->decorate($tx, $el); + + // add
+ if (strpos($content, "\r") !== FALSE) { + $key = $tx->protect('
', Texy::CONTENT_REPLACED); + $content = str_replace("\r", $key, $content); + }; + } + + $content = strtr($content, "\r\n", ' '); + $el->setText($content); + + return $el; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyPhraseModule.php b/apigen/libs/Texy/texy/modules/TexyPhraseModule.php new file mode 100644 index 00000000000..24a1a5dc8b5 --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyPhraseModule.php @@ -0,0 +1,334 @@ + 'strong', // or 'b' + 'phrase/em' => 'em', // or 'i' + 'phrase/em-alt' => 'em', + 'phrase/em-alt2' => 'em', + 'phrase/ins' => 'ins', + 'phrase/del' => 'del', + 'phrase/sup' => 'sup', + 'phrase/sup-alt' => 'sup', + 'phrase/sub' => 'sub', + 'phrase/sub-alt' => 'sub', + 'phrase/span' => 'a', + 'phrase/span-alt' => 'a', + 'phrase/cite' => 'cite', + 'phrase/acronym' => 'acronym', + 'phrase/acronym-alt' => 'acronym', + 'phrase/code' => 'code', + 'phrase/quote' => 'q', + 'phrase/quicklink' => 'a', + ); + + + /** @var bool are links allowed? */ + public $linksAllowed = TRUE; + + + + public function __construct($texy) + { + $this->texy = $texy; + + $texy->addHandler('phrase', array($this, 'solve')); + +/* + // UNIVERSAL + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#((?>([*+/^_"~`-])+?))(?!\s)(.*(?!\\2).)'.TEXY_MODIFIER.'?(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?()"\''.TEXY_MARK.'-])\*(?![\s*])(.+)'.TEXY_MODIFIER.'?(?()"?!\'-])'.TEXY_LINK.'??()#Uus', + 'phrase/em-alt2' + ); + + // ++inserted++ + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?-])([^\r\n]+)'.TEXY_MODIFIER.'?(?-])()#Uu', + 'phrase/del' + ); + + // ^^superscript^^ + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternSupSub'), + '#(?<=[a-z0-9])\^([n0-9+-]{1,4}?)(?![a-z0-9])#Uui', + 'phrase/sup-alt' + ); + + // __subscript__ + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternSupSub'), + '#(?<=[a-z])\_([n0-9]{1,3})(?![a-z0-9])#Uui', + 'phrase/sub-alt' + ); + + // "span" + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?>quote<< + $texy->registerLinePattern( + array($this, 'patternPhrase'), + '#(?)\>\>(?![\s>])([^\r\n]+)'.TEXY_MODIFIER.'?(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#(?registerLinePattern( + array($this, 'patternNoTexy'), + '#(?registerLinePattern( + array($this, 'patternPhrase'), + '#\`(\S[^'.TEXY_MARK.'\r\n]*)'.TEXY_MODIFIER.'?(?registerLinePattern( + array($this, 'patternPhrase'), + '#(['.TEXY_CHAR.'0-9@\#$%&.,_-]+)()(?=:\[)'.TEXY_LINK.'()#Uu', + 'phrase/quicklink' + ); + + + $texy->allowed['phrase/ins'] = FALSE; + $texy->allowed['phrase/del'] = FALSE; + $texy->allowed['phrase/sup'] = FALSE; + $texy->allowed['phrase/sub'] = FALSE; + $texy->allowed['phrase/cite'] = FALSE; + } + + + + /** + * Callback for: **.... .(title)[class]{style}**:LINK. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternPhrase($parser, $matches, $phrase) + { + list(, $mContent, $mMod, $mLink) = $matches; + // [1] => ** + // [2] => ... + // [3] => .(title)[class]{style} + // [4] => LINK + + $tx = $this->texy; + $mod = new TexyModifier($mMod); + $link = NULL; + + $parser->again = $phrase !== 'phrase/code' && $phrase !== 'phrase/quicklink'; + + if ($phrase === 'phrase/span' || $phrase === 'phrase/span-alt') { + if ($mLink == NULL) { + if (!$mMod) return FALSE; // means "..." + } else { + $link = $tx->linkModule->factoryLink($mLink, $mMod, $mContent); + } + + } elseif ($phrase === 'phrase/acronym' || $phrase === 'phrase/acronym-alt') { + $mod->title = trim(Texy::unescapeHtml($mLink)); + + } elseif ($phrase === 'phrase/quote') { + $mod->cite = $tx->blockQuoteModule->citeLink($mLink); + + } elseif ($mLink != NULL) { + $link = $tx->linkModule->factoryLink($mLink, NULL, $mContent); + } + + return $tx->invokeAroundHandlers('phrase', $parser, array($phrase, $mContent, $mod, $link)); + } + + + + /** + * Callback for: any^2 any_2. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternSupSub($parser, $matches, $phrase) + { + list(, $mContent) = $matches; + $mod = new TexyModifier(); + $link = NULL; + $mContent = str_replace('-', "\xE2\x88\x92", $mContent); // − + return $this->texy->invokeAroundHandlers('phrase', $parser, array($phrase, $mContent, $mod, $link)); + } + + + + /** + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return string + */ + public function patternNoTexy($parser, $matches) + { + list(, $mContent) = $matches; + return $this->texy->protect(Texy::escapeHtml($mContent), Texy::CONTENT_TEXTUAL); + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string + * @param string + * @param TexyModifier + * @param TexyLink + * @return TexyHtml + */ + public function solve($invocation, $phrase, $content, $mod, $link) + { + $tx = $this->texy; + + $tag = isset($this->tags[$phrase]) ? $this->tags[$phrase] : NULL; + + if ($tag === 'a') + $tag = $link && $this->linksAllowed ? NULL : 'span'; + + if ($phrase === 'phrase/code') + $content = $tx->protect(Texy::escapeHtml($content), Texy::CONTENT_TEXTUAL); + + if ($phrase === 'phrase/strong+em') { + $el = TexyHtml::el($this->tags['phrase/strong']); + $el->create($this->tags['phrase/em'], $content); + $mod->decorate($tx, $el); + + } elseif ($tag) { + $el = TexyHtml::el($tag)->setText($content); + $mod->decorate($tx, $el); + + if ($tag === 'q') $el->attrs['cite'] = $mod->cite; + } else { + $el = $content; // trick + } + + if ($link && $this->linksAllowed) return $tx->linkModule->solve(NULL, $link, $el); + + return $el; + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyScriptModule.php b/apigen/libs/Texy/texy/modules/TexyScriptModule.php new file mode 100644 index 00000000000..8b1efa630dc --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyScriptModule.php @@ -0,0 +1,119 @@ +texy = $texy; + + $texy->addHandler('script', array($this, 'solve')); + + $texy->registerLinePattern( + array($this, 'pattern'), + '#\{\{([^'.TEXY_MARK.']+)\}\}()#U', + 'script' + ); + } + + + + /** + * Callback for: {{...}}. + * + * @param TexyLineParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function pattern($parser, $matches) + { + list(, $mContent) = $matches; + // [1] => ... + + $cmd = trim($mContent); + if ($cmd === '') return FALSE; + + $args = $raw = NULL; + // function(arg, arg, ...) or function: arg, arg + if (preg_match('#^([a-z_][a-z0-9_-]*)\s*(?:\(([^()]*)\)|:(.*))$#iu', $cmd, $matches)) { + $cmd = $matches[1]; + $raw = isset($matches[3]) ? trim($matches[3]) : trim($matches[2]); + if ($raw === '') + $args = array(); + else + $args = preg_split('#\s*' . preg_quote($this->separator, '#') . '\s*#u', $raw); + } + + // Texy 1.x way + if ($this->handler) { + if (is_callable(array($this->handler, $cmd))) { + array_unshift($args, $parser); + return call_user_func_array(array($this->handler, $cmd), $args); + } + + if (is_callable($this->handler)) + return call_user_func_array($this->handler, array($parser, $cmd, $args, $raw)); + } + + // Texy 2 way + return $this->texy->invokeAroundHandlers('script', $parser, array($cmd, $args, $raw)); + } + + + + /** + * Finish invocation. + * + * @param TexyHandlerInvocation handler invocation + * @param string command + * @param array arguments + * @param string arguments in raw format + * @return TexyHtml|string|FALSE + */ + public function solve($invocation, $cmd, $args, $raw) + { + if ($cmd === 'texy') { + if (!$args) return FALSE; + + switch ($args[0]) { + case 'nofollow': + $this->texy->linkModule->forceNoFollow = TRUE; + break; + } + return ''; + + } else { + return FALSE; + } + } + +} diff --git a/apigen/libs/Texy/texy/modules/TexyTableModule.php b/apigen/libs/Texy/texy/modules/TexyTableModule.php new file mode 100644 index 00000000000..7279b9912e9 --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyTableModule.php @@ -0,0 +1,309 @@ +texy = $texy; + + $texy->registerBlockPattern( + array($this, 'patternTable'), + '#^(?:'.TEXY_MODIFIER_HV.'\n)?' // .{color: red} + . '\|.*()$#mU', // | .... + 'table' + ); + } + + + + /** + * Callback for:. + * + * .(title)[class]{style}> + * |------------------ + * | xxx | xxx | xxx | .(..){..}[..] + * |------------------ + * | aa | bb | cc | + * + * @param TexyBlockParser + * @param array regexp matches + * @param string pattern name + * @return TexyHtml|string|FALSE + */ + public function patternTable($parser, $matches) + { + if ($this->disableTables) return FALSE; + list(, $mMod) = $matches; + // [1] => .(title)[class]{style}<>_ + + $tx = $this->texy; + + $el = TexyHtml::el('table'); + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $el); + + $parser->moveBackward(); + + if ($parser->next('#^\|(\#|\=){2,}(?![|\#=+])(.+)\\1*\|? *'.TEXY_MODIFIER_H.'?()$#Um', $matches)) { + list(, , $mContent, $mMod) = $matches; + // [1] => # / = + // [2] => .... + // [3] => .(title)[class]{style}<> + + $caption = $el->create('caption'); + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $caption); + $caption->parseLine($tx, $mContent); + } + + $isHead = FALSE; + $colModifier = array(); + $prevRow = array(); // rowSpan building helper + $rowCounter = 0; + $colCounter = 0; + $elPart = NULL; + $lineMode = FALSE; // rows must be separated by lines + + while (TRUE) { + if ($parser->next('#^\|([=-])[+|=-]{2,}$#Um', $matches)) { // line + if ($lineMode) { + if ($matches[1] === '=') $isHead = !$isHead; + } else { + $isHead = !$isHead; + $lineMode = $matches[1] === '='; + } + $prevRow = array(); + continue; + } + + if ($parser->next('#^\|(.*)(?:|\|\ *'.TEXY_MODIFIER_HV.'?)()$#U', $matches)) { + // smarter head detection + if ($rowCounter === 0 && !$isHead && $parser->next('#^\|[=-][+|=-]{2,}$#Um', $foo)) { + $isHead = TRUE; + $parser->moveBackward(); + } + + if ($elPart === NULL) { + $elPart = $el->create($isHead ? 'thead' : 'tbody'); + + } elseif (!$isHead && $elPart->getName() === 'thead') { + $this->finishPart($elPart); + $elPart = $el->create('tbody'); + } + + + // PARSE ROW + list(, $mContent, $mMod) = $matches; + // [1] => .... + // [2] => .(title)[class]{style}<>_ + + $elRow = TexyHtml::el('tr'); + $mod = new TexyModifier($mMod); + $mod->decorate($tx, $elRow); + + $rowClass = $rowCounter % 2 === 0 ? $this->oddClass : $this->evenClass; + if ($rowClass && !isset($mod->classes[$this->oddClass]) && !isset($mod->classes[$this->evenClass])) { + $elRow->attrs['class'][] = $rowClass; + } + + $col = 0; + $elCell = NULL; + + // special escape sequence \| + $mContent = str_replace('\\|', "\x13", $mContent); + $mContent = preg_replace('#(\[[^\]]*)\|#', "$1\x13", $mContent); // HACK: support for [..|..] + + foreach (explode('|', $mContent) as $cell) { + $cell = strtr($cell, "\x13", '|'); + // rowSpan + if (isset($prevRow[$col]) && ($lineMode || preg_match('#\^\ *$|\*??(.*)\ +\^$#AU', $cell, $matches))) { + $prevRow[$col]->rowSpan++; + if (!$lineMode) { + $cell = isset($matches[1]) ? $matches[1] : ''; + } + $prevRow[$col]->text .= "\n" . $cell; + $col += $prevRow[$col]->colSpan; + $elCell = NULL; + continue; + } + + // colSpan + if ($cell === '' && $elCell) { + $elCell->colSpan++; + unset($prevRow[$col]); + $col++; + continue; + } + + // common cell + if (!preg_match('#(\*??)\ *'.TEXY_MODIFIER_HV.'??(.*)'.TEXY_MODIFIER_HV.'?\ *()$#AU', $cell, $matches)) continue; + list(, $mHead, $mModCol, $mContent, $mMod) = $matches; + // [1] => * ^ + // [2] => .(title)[class]{style}<>_ + // [3] => .... + // [4] => .(title)[class]{style}<>_ + + if ($mModCol) { + $colModifier[$col] = new TexyModifier($mModCol); + } + + if (isset($colModifier[$col])) + $mod = clone $colModifier[$col]; + else + $mod = new TexyModifier; + + $mod->setProperties($mMod); + + $elCell = new TexyTableCellElement; + $elCell->setName($isHead || ($mHead === '*') ? 'th' : 'td'); + $mod->decorate($tx, $elCell); + $elCell->text = $mContent; + + $elRow->add($elCell); + $prevRow[$col] = $elCell; + $col++; + } + + + // even up with empty cells + while ($col < $colCounter) { + if (isset($prevRow[$col]) && $lineMode) { + $prevRow[$col]->rowSpan++; + $prevRow[$col]->text .= "\n"; + + } else { + $elCell = new TexyTableCellElement; + $elCell->setName($isHead ? 'th' : 'td'); + if (isset($colModifier[$col])) { + $colModifier[$col]->decorate($tx, $elCell); + } + $elRow->add($elCell); + $prevRow[$col] = $elCell; + } + $col++; + } + $colCounter = $col; + + + if ($elRow->count()) { + $elPart->add($elRow); + $rowCounter++; + } else { + // redundant row + foreach ($prevRow as $elCell) $elCell->rowSpan--; + } + + continue; + } + + break; + } + + if ($elPart === NULL) { + // invalid table + return FALSE; + } + + if ($elPart->getName() === 'thead') { + // thead is optional, tbody is required + $elPart->setName('tbody'); + } + + $this->finishPart($elPart); + + + // event listener + $tx->invokeHandlers('afterTable', array($parser, $el, $mod)); + + return $el; + } + + + + /** + * Parse text in all cells. + * @param TexyHtml + * @return void + */ + private function finishPart($elPart) + { + $tx = $this->texy; + + foreach ($elPart->getChildren() as $elRow) + { + foreach ($elRow->getChildren() as $elCell) + { + if ($elCell->colSpan > 1) { + $elCell->attrs['colspan'] = $elCell->colSpan; + } + + if ($elCell->rowSpan > 1) { + $elCell->attrs['rowspan'] = $elCell->rowSpan; + } + + $text = rtrim($elCell->text); + if (strpos($text, "\n") !== FALSE) { + // multiline parse as block + // HACK: disable tables + $this->disableTables = TRUE; + $elCell->parseBlock($tx, Texy::outdent($text)); + $this->disableTables = FALSE; + } else { + $elCell->parseLine($tx, ltrim($text)); + } + + if ($elCell->getText() === '') { + $elCell->setText("\xC2\xA0"); //   + } + } + } + } + +} + + + + +/** + * Table cell TD / TH. + * @package Texy + */ +class TexyTableCellElement extends TexyHtml +{ + /** @var int */ + public $colSpan = 1; + + /** @var int */ + public $rowSpan = 1; + + /** @var string */ + public $text; + +} diff --git a/apigen/libs/Texy/texy/modules/TexyTypographyModule.php b/apigen/libs/Texy/texy/modules/TexyTypographyModule.php new file mode 100644 index 00000000000..e4ad662fa85 --- /dev/null +++ b/apigen/libs/Texy/texy/modules/TexyTypographyModule.php @@ -0,0 +1,139 @@ + array( + 'singleQuotes' => array("\xe2\x80\x9a", "\xe2\x80\x98"), // U+201A, U+2018 + 'doubleQuotes' => array("\xe2\x80\x9e", "\xe2\x80\x9c"), // U+201E, U+201C + ), + + 'en' => array( + 'singleQuotes' => array("\xe2\x80\x98", "\xe2\x80\x99"), // U+2018, U+2019 + 'doubleQuotes' => array("\xe2\x80\x9c", "\xe2\x80\x9d"), // U+201C, U+201D + ), + + 'fr' => array( + 'singleQuotes' => array("\xe2\x80\xb9", "\xe2\x80\xba"), // U+2039, U+203A + 'doubleQuotes' => array("\xc2\xab", "\xc2\xbb"), // U+00AB, U+00BB + ), + + 'de' => array( + 'singleQuotes' => array("\xe2\x80\x9a", "\xe2\x80\x98"), // U+201A, U+2018 + 'doubleQuotes' => array("\xe2\x80\x9e", "\xe2\x80\x9c"), // U+201E, U+201C + ), + + 'pl' => array( + 'singleQuotes' => array("\xe2\x80\x9a", "\xe2\x80\x99"), // U+201A, U+2019 + 'doubleQuotes' => array("\xe2\x80\x9e", "\xe2\x80\x9d"), // U+201E, U+201D + ), + ); + + /** @var string */ + public $locale = 'cs'; + + /** @var array */ + private $pattern, $replace; + + + + public function __construct($texy) + { + $this->texy = $texy; + $texy->registerPostLine(array($this, 'postLine'), 'typography'); + $texy->addHandler('beforeParse', array($this, 'beforeParse')); + } + + + + /** + * Text pre-processing. + * @param Texy + * @param string + * @return void + */ + public function beforeParse($texy, & $text) + { + // CONTENT_MARKUP mark: \x17-\x1F + // CONTENT_REPLACED mark: \x16 + // CONTENT_TEXTUAL mark: \x17 + // CONTENT_BLOCK: not used in postLine + + if (isset(self::$locales[$this->locale])) + $locale = self::$locales[$this->locale]; + else // fall back + $locale = self::$locales['en']; + + $pairs = array( + '#(? "\xe2\x80\xa6", // ellipsis ... + '#(?<=[\d ]|^)-(?=[\d ]|$)#' => "\xe2\x80\x93", // en dash 123-123 + '#(?<=[^!*+,/:;<=>@\\\\_|-])--(?=[^!*+,/:;<=>@\\\\_|-])#' => "\xe2\x80\x93", // en dash alphanum--alphanum + '#,-#' => ",\xe2\x80\x93", // en dash ,- + '#(? "\$1\xc2\xa0\$2\xc2\xa0\$3", // date 23. 1. 1978 + '#(? "\$1\xc2\xa0\$2", // date 23. 1. + '# --- #' => "\xc2\xa0\xe2\x80\x94 ", // em dash --- + '# ([\x{2013}\x{2014}])#u' => "\xc2\xa0\$1", //   behind dash (dash stays at line end) + '# <-{1,2}> #' => " \xe2\x86\x94 ", // left right arrow <--> + '#-{1,}> #' => " \xe2\x86\x92 ", // right arrow --> + '# <-{1,}#' => " \xe2\x86\x90 ", // left arrow <-- + '#={1,}> #' => " \xe2\x87\x92 ", // right arrow ==> + '#\\+-#' => "\xc2\xb1", // +- + '#(\d+)( ?)x\\2(?=\d)#' => "\$1\xc3\x97", // dimension sign 123 x 123... + '#(?<=\d)x(?= |,|.|$)#m' => "\xc3\x97", // dimension sign 123x + '#(\S ?)\(TM\)#i' => "\$1\xe2\x84\xa2", // trademark (TM) + '#(\S ?)\(R\)#i' => "\$1\xc2\xae", // registered (R) + '#\(C\)( ?\S)#i' => "\xc2\xa9\$1", // copyright (C) + '#\(EUR\)#' => "\xe2\x82\xac", // Euro (EUR) + '#(\d) (?=\d{3})#' => "\$1\xc2\xa0", // (phone) number 1 123 123 123... + + '#(?<=[^\s\x17])\s+([\x17-\x1F]+)(?=\s)#u'=> "\$1", // remove intermarkup space phase 1 + '#(?<=\s)([\x17-\x1F]+)\s+#u' => "\$1", // remove intermarkup space phase 2 + + '#(?<=.{50})\s+(?=[\x17-\x1F]*\S{1,6}[\x17-\x1F]*$)#us' => "\xc2\xa0", // space before last short word + + // nbsp space between number (optionally followed by dot) and word, symbol, punctation, currency symbol + '#(?<=^| |\.|,|-|\+|\x16|\(|\d\x{A0})([\x17-\x1F]*\d+\.?[\x17-\x1F]*)\s+(?=[\x17-\x1F]*[%'.TEXY_CHAR.'\x{b0}-\x{be}\x{2020}-\x{214f}])#mu' + => "\$1\xc2\xa0", + + // space between preposition and word + '#(?<=^|[^0-9'.TEXY_CHAR.'])([\x17-\x1F]*[ksvzouiKSVZOUIA][\x17-\x1F]*)\s+(?=[\x17-\x1F]*[0-9'.TEXY_CHAR.'])#mus' + => "\$1\xc2\xa0", + + '#(? $locale['doubleQuotes'][0].'$1'.$locale['doubleQuotes'][1], // double "" + '#(? $locale['singleQuotes'][0].'$1'.$locale['singleQuotes'][1], // single '' + ); + + $this->pattern = array_keys($pairs); + $this->replace = array_values($pairs); + } + + + + public function postLine($text, $preserveSpaces = FALSE) + { + if (!$preserveSpaces) { + $text = preg_replace('# {2,}#', ' ', $text); + } + return preg_replace($this->pattern, $this->replace, $text); + } + +} \ No newline at end of file diff --git a/apigen/libs/Texy/texy/netterobots.txt b/apigen/libs/Texy/texy/netterobots.txt new file mode 100644 index 00000000000..45b983866bc --- /dev/null +++ b/apigen/libs/Texy/texy/netterobots.txt @@ -0,0 +1 @@ +Disallow: /modules diff --git a/apigen/libs/Texy/texy/texy.php b/apigen/libs/Texy/texy/texy.php new file mode 100644 index 00000000000..58ff07e01bb --- /dev/null +++ b/apigen/libs/Texy/texy/texy.php @@ -0,0 +1,965 @@ + + * $texy = new Texy(); + * $html = $texy->process($text); + *
+ * + * @copyright Copyright (c) 2004, 2010 David Grudl + * @package Texy + */ +class Texy extends TexyObject +{ + // configuration directives + const ALL = TRUE; + const NONE = FALSE; + + // Texy version + const VERSION = TEXY_VERSION; + const REVISION = '$WCREV$ released on $WCDATE$'; + + // types of protection marks + const CONTENT_MARKUP = "\x17"; + const CONTENT_REPLACED = "\x16"; + const CONTENT_TEXTUAL = "\x15"; + const CONTENT_BLOCK = "\x14"; + + // url filters + const FILTER_ANCHOR = 'anchor'; + const FILTER_IMAGE = 'image'; + + // HTML minor-modes + const XML = 2; + + // HTML modes + const HTML4_TRANSITIONAL = 0; + const HTML4_STRICT = 1; + const HTML5 = 4; + const XHTML1_TRANSITIONAL = 2; // Texy::HTML4_TRANSITIONAL | Texy::XML; + const XHTML1_STRICT = 3; // Texy::HTML4_STRICT | Texy::XML; + const XHTML5 = 6; // Texy::HTML5 | Texy::XML; + + /** @var string input & output text encoding */ + public $encoding = 'utf-8'; + + /** @var array Texy! syntax configuration */ + public $allowed = array(); + + /** @var TRUE|FALSE|array Allowed HTML tags */ + public $allowedTags; + + /** @var TRUE|FALSE|array Allowed classes */ + public $allowedClasses = Texy::ALL; // all classes and id are allowed + + /** @var TRUE|FALSE|array Allowed inline CSS style */ + public $allowedStyles = Texy::ALL; // all inline styles are allowed + + /** @var int TAB width (for converting tabs to spaces) */ + public $tabWidth = 8; + + /** @var boolean Do obfuscate e-mail addresses? */ + public $obfuscateEmail = TRUE; + + /** @var array regexps to check URL schemes */ + public $urlSchemeFilters = NULL; // disable URL scheme filter + + /** @var bool Paragraph merging mode */ + public $mergeLines = TRUE; + + /** @var array Parsing summary */ + public $summary = array( + 'images' => array(), + 'links' => array(), + 'preload' => array(), + ); + + /** @var string Generated stylesheet */ + public $styleSheet = ''; + + /** @var array CSS classes for align modifiers */ + public $alignClasses = array( + 'left' => NULL, + 'right' => NULL, + 'center' => NULL, + 'justify' => NULL, + 'top' => NULL, + 'middle' => NULL, + 'bottom' => NULL, + ); + + /** @var bool remove soft hyphens (SHY)? */ + public $removeSoftHyphens = TRUE; + + /** @var mixed */ + public static $advertisingNotice = 'once'; + + /** @var string */ + public $nontextParagraph = 'div'; + + /** @var TexyScriptModule */ + public $scriptModule; + + /** @var TexyParagraphModule */ + public $paragraphModule; + + /** @var TexyHtmlModule */ + public $htmlModule; + + /** @var TexyImageModule */ + public $imageModule; + + /** @var TexyLinkModule */ + public $linkModule; + + /** @var TexyPhraseModule */ + public $phraseModule; + + /** @var TexyEmoticonModule */ + public $emoticonModule; + + /** @var TexyBlockModule */ + public $blockModule; + + /** @var TexyHeadingModule */ + public $headingModule; + + /** @var TexyHorizLineModule */ + public $horizLineModule; + + /** @var TexyBlockQuoteModule */ + public $blockQuoteModule; + + /** @var TexyListModule */ + public $listModule; + + /** @var TexyTableModule */ + public $tableModule; + + /** @var TexyFigureModule */ + public $figureModule; + + /** @var TexyTypographyModule */ + public $typographyModule; + + /** @var TexyLongWordsModule */ + public $longWordsModule; + + /** @var TexyHtmlOutputModule */ + public $htmlOutputModule; + + + /** + * Registered regexps and associated handlers for inline parsing. + * @var array of ('handler' => callback + * 'pattern' => regular expression) + */ + private $linePatterns = array(); + private $_linePatterns; + + /** + * Registered regexps and associated handlers for block parsing. + * @var array of ('handler' => callback + * 'pattern' => regular expression) + */ + private $blockPatterns = array(); + private $_blockPatterns; + + /** @var array */ + private $postHandlers = array(); + + /** @var TexyHtml DOM structure for parsed text */ + private $DOM; + + /** @var array Texy protect markup table */ + private $marks = array(); + + /** @var array for internal usage */ + public $_classes, $_styles; + + /** @var bool */ + private $processing; + + /** @var array of events and registered handlers */ + private $handlers = array(); + + /** + * DTD descriptor. + * $dtd[element][0] - allowed attributes (as array keys) + * $dtd[element][1] - allowed content for an element (content model) (as array keys) + * - array of allowed elements (as keys) + * - FALSE - empty element + * - 0 - special case for ins & del + * @var array + */ + public $dtd; + + /** @var array */ + private static $dtdCache; + + /** @var int HTML mode */ + private $mode; + + + /** DEPRECATED */ + public static $strictDTD; + public $cleaner; + public $xhtml; + + + + public function __construct() + { + // load all modules + $this->loadModules(); + + // DEPRECATED + if (self::$strictDTD !== NULL) { + $this->setOutputMode(self::$strictDTD ? self::XHTML1_STRICT : self::XHTML1_TRANSITIONAL); + } else { + $this->setOutputMode(self::XHTML1_TRANSITIONAL); + } + + // DEPRECATED + $this->cleaner = & $this->htmlOutputModule; + + // examples of link references ;-) + $link = new TexyLink('http://texy.info/'); + $link->modifier->title = 'The best text -> HTML converter and formatter'; + $link->label = 'Texy!'; + $this->linkModule->addReference('texy', $link); + + $link = new TexyLink('http://www.google.com/search?q=%s'); + $this->linkModule->addReference('google', $link); + + $link = new TexyLink('http://en.wikipedia.org/wiki/Special:Search?search=%s'); + $this->linkModule->addReference('wikipedia', $link); + } + + + + /** + * Set HTML/XHTML output mode (overwrites self::$allowedTags) + * @param int + * @return void + */ + public function setOutputMode($mode) + { + if (!in_array($mode, array(self::HTML4_TRANSITIONAL, self::HTML4_STRICT, + self::HTML5, self::XHTML1_TRANSITIONAL, self::XHTML1_STRICT, self::XHTML5), TRUE)) { + throw new InvalidArgumentException("Invalid mode."); + } + + if (!isset(self::$dtdCache[$mode])) { + require dirname(__FILE__) . '/libs/DTD.php'; + self::$dtdCache[$mode] = $dtd; + } + + $this->mode = $mode; + $this->dtd = self::$dtdCache[$mode]; + TexyHtml::$xhtml = (bool) ($mode & self::XML); // TODO: remove? + + // accept all valid HTML tags and attributes by default + $this->allowedTags = array(); + foreach ($this->dtd as $tag => $dtd) { + $this->allowedTags[$tag] = self::ALL; + } + } + + + + /** + * Get HTML/XHTML output mode + * @return int + */ + public function getOutputMode() + { + return $this->mode; + } + + + + /** + * Create array of all used modules ($this->modules). + * This array can be changed by overriding this method (by subclasses) + */ + protected function loadModules() + { + // line parsing + $this->scriptModule = new TexyScriptModule($this); + $this->htmlModule = new TexyHtmlModule($this); + $this->imageModule = new TexyImageModule($this); + $this->phraseModule = new TexyPhraseModule($this); + $this->linkModule = new TexyLinkModule($this); + $this->emoticonModule = new TexyEmoticonModule($this); + + // block parsing + $this->paragraphModule = new TexyParagraphModule($this); + $this->blockModule = new TexyBlockModule($this); + $this->figureModule = new TexyFigureModule($this); + $this->horizLineModule = new TexyHorizLineModule($this); + $this->blockQuoteModule = new TexyBlockQuoteModule($this); + $this->tableModule = new TexyTableModule($this); + $this->headingModule = new TexyHeadingModule($this); + $this->listModule = new TexyListModule($this); + + // post process + $this->typographyModule = new TexyTypographyModule($this); + $this->longWordsModule = new TexyLongWordsModule($this); + $this->htmlOutputModule = new TexyHtmlOutputModule($this); + } + + + + final public function registerLinePattern($handler, $pattern, $name, $againTest = NULL) + { + if (!is_callable($handler)) { + $able = is_callable($handler, TRUE, $textual); + throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.')); + } + + if (!isset($this->allowed[$name])) $this->allowed[$name] = TRUE; + + $this->linePatterns[$name] = array( + 'handler' => $handler, + 'pattern' => $pattern, + 'again' => $againTest, + ); + } + + + + final public function registerBlockPattern($handler, $pattern, $name) + { + if (!is_callable($handler)) { + $able = is_callable($handler, TRUE, $textual); + throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.')); + } + + // if (!preg_match('#(.)\^.*\$\\1[a-z]*#is', $pattern)) die("Texy: Not a block pattern $name"); + if (!isset($this->allowed[$name])) $this->allowed[$name] = TRUE; + + $this->blockPatterns[$name] = array( + 'handler' => $handler, + 'pattern' => $pattern . 'm', // force multiline + ); + } + + + + final public function registerPostLine($handler, $name) + { + if (!is_callable($handler)) { + $able = is_callable($handler, TRUE, $textual); + throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.')); + } + + if (!isset($this->allowed[$name])) $this->allowed[$name] = TRUE; + + $this->postHandlers[$name] = $handler; + } + + + + /** + * Converts document in Texy! to (X)HTML code. + * + * @param string input text + * @param bool is single line? + * @return string output HTML code + */ + public function process($text, $singleLine = FALSE) + { + if ($this->processing) { + throw new InvalidStateException('Processing is in progress yet.'); + } + + // initialization + $this->marks = array(); + $this->processing = TRUE; + + // speed-up + if (is_array($this->allowedClasses)) $this->_classes = array_flip($this->allowedClasses); + else $this->_classes = $this->allowedClasses; + + if (is_array($this->allowedStyles)) $this->_styles = array_flip($this->allowedStyles); + else $this->_styles = $this->allowedStyles; + + // convert to UTF-8 (and check source encoding) + $text = TexyUtf::toUtf($text, $this->encoding); + + if ($this->removeSoftHyphens) { + $text = str_replace("\xC2\xAD", '', $text); + } + + // standardize line endings and spaces + $text = self::normalize($text); + + // replace tabs with spaces + $this->tabWidth = max(1, (int) $this->tabWidth); + while (strpos($text, "\t") !== FALSE) { + $text = preg_replace_callback('#^(.*)\t#mU', array($this, 'tabCb'), $text); + } + + // user before handler + $this->invokeHandlers('beforeParse', array($this, & $text, $singleLine)); + + // select patterns + $this->_linePatterns = $this->linePatterns; + $this->_blockPatterns = $this->blockPatterns; + foreach ($this->_linePatterns as $name => $foo) { + if (empty($this->allowed[$name])) unset($this->_linePatterns[$name]); + } + foreach ($this->_blockPatterns as $name => $foo) { + if (empty($this->allowed[$name])) unset($this->_blockPatterns[$name]); + } + + // parse Texy! document into internal DOM structure + $this->DOM = TexyHtml::el(); + if ($singleLine) { + $this->DOM->parseLine($this, $text); + } else { + $this->DOM->parseBlock($this, $text); + } + + // user after handler + $this->invokeHandlers('afterParse', array($this, $this->DOM, $singleLine)); + + // converts internal DOM structure to final HTML code + $html = $this->DOM->toHtml($this); + + // this notice should remain + if (self::$advertisingNotice) { + $html .= "\n"; + if (self::$advertisingNotice === 'once') { + self::$advertisingNotice = FALSE; + } + } + + $this->processing = FALSE; + + return TexyUtf::utf2html($html, $this->encoding); + } + + + + /** + * Converts single line in Texy! to (X)HTML code. + * + * @param string input text + * @return string output HTML code + */ + public function processLine($text) + { + return $this->process($text, TRUE); + } + + + + /** + * Makes only typographic corrections. + * @param string input text (in encoding defined by Texy::$encoding) + * @return string output text (in UTF-8) + */ + public function processTypo($text) + { + // convert to UTF-8 (and check source encoding) + $text = TexyUtf::toUtf($text, $this->encoding); + + // standardize line endings and spaces + $text = self::normalize($text); + + $this->typographyModule->beforeParse($this, $text); + $text = $this->typographyModule->postLine($text, TRUE); + + if (!empty($this->allowed['longwords'])) { + $text = $this->longWordsModule->postLine($text); + } + + return TexyUtf::utf2html($text, $this->encoding); + } + + + + /** + * Converts DOM structure to pure text. + * @return string + */ + public function toText() + { + if (!$this->DOM) { + throw new InvalidStateException('Call $texy->process() first.'); + } + + return TexyUtf::utfTo($this->DOM->toText($this), $this->encoding); + } + + + + /** + * Converts internal string representation to final HTML code in UTF-8. + * @return string + */ + final public function stringToHtml($s) + { + // decode HTML entities to UTF-8 + $s = self::unescapeHtml($s); + + // line-postprocessing + $blocks = explode(self::CONTENT_BLOCK, $s); + foreach ($this->postHandlers as $name => $handler) { + if (empty($this->allowed[$name])) continue; + foreach ($blocks as $n => $s) { + if ($n % 2 === 0 && $s !== '') { + $blocks[$n] = call_user_func($handler, $s); + } + } + } + $s = implode(self::CONTENT_BLOCK, $blocks); + + // encode < > & + $s = self::escapeHtml($s); + + // replace protected marks + $s = $this->unProtect($s); + + // wellform and reformat HTML + $this->invokeHandlers('postProcess', array($this, & $s)); + + // unfreeze spaces + $s = self::unfreezeSpaces($s); + + return $s; + } + + + + /** + * Converts internal string representation to final HTML code in UTF-8. + * @return string + */ + final public function stringToText($s) + { + $save = $this->htmlOutputModule->lineWrap; + $this->htmlOutputModule->lineWrap = FALSE; + $s = $this->stringToHtml( $s ); + $this->htmlOutputModule->lineWrap = $save; + + // remove tags + $s = preg_replace('#<(script|style)(.*)#Uis', '', $s); + $s = strip_tags($s); + $s = preg_replace('#\n\s*\n\s*\n[\n\s]*\n#', "\n\n", $s); + + // entities -> chars + $s = self::unescapeHtml($s); + + // convert nbsp to normal space and remove shy + $s = strtr($s, array( + "\xC2\xAD" => '', // shy + "\xC2\xA0" => ' ', // nbsp + )); + + return $s; + } + + + + /** + * Add new event handler. + * + * @param string event name + * @param callback + * @return void + */ + final public function addHandler($event, $callback) + { + if (!is_callable($callback)) { + $able = is_callable($callback, TRUE, $textual); + throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.')); + } + + $this->handlers[$event][] = $callback; + } + + + + /** + * Invoke registered around-handlers. + * + * @param string event name + * @param TexyParser actual parser object + * @param array arguments passed into handler + * @return mixed + */ + final public function invokeAroundHandlers($event, $parser, $args) + { + if (!isset($this->handlers[$event])) return FALSE; + + $invocation = new TexyHandlerInvocation($this->handlers[$event], $parser, $args); + $res = $invocation->proceed(); + $invocation->free(); + return $res; + } + + + + /** + * Invoke registered after-handlers. + * + * @param string event name + * @param array arguments passed into handler + * @return void + */ + final public function invokeHandlers($event, $args) + { + if (!isset($this->handlers[$event])) return; + + foreach ($this->handlers[$event] as $handler) { + call_user_func_array($handler, $args); + } + } + + + + /** + * Translate all white spaces (\t \n \r space) to meta-spaces \x01-\x04. + * which are ignored by TexyHtmlOutputModule routine + * @param string + * @return string + */ + final public static function freezeSpaces($s) + { + return strtr($s, " \t\r\n", "\x01\x02\x03\x04"); + } + + + + /** + * Reverts meta-spaces back to normal spaces. + * @param string + * @return string + */ + final public static function unfreezeSpaces($s) + { + return strtr($s, "\x01\x02\x03\x04", " \t\r\n"); + } + + + + /** + * Removes special controls characters and normalizes line endings and spaces. + * @param string + * @return string + */ + final public static function normalize($s) + { + // standardize line endings to unix-like + $s = str_replace("\r\n", "\n", $s); // DOS + $s = strtr($s, "\r", "\n"); // Mac + + // remove special chars; leave \t + \n + $s = preg_replace('#[\x00-\x08\x0B-\x1F]+#', '', $s); + + // right trim + $s = preg_replace("#[\t ]+$#m", '', $s); + + // trailing spaces + $s = trim($s, "\n"); + + return $s; + } + + + + /** + * Converts to web safe characters [a-z0-9-] text. + * @param string + * @param string + * @return string + */ + final public static function webalize($s, $charlist = NULL) + { + $s = TexyUtf::utf2ascii($s); + $s = strtolower($s); + $s = preg_replace('#[^a-z0-9'.preg_quote($charlist, '#').']+#', '-', $s); + $s = trim($s, '-'); + return $s; + } + + + + /** + * Texy! version of htmlSpecialChars (much faster than htmlSpecialChars!). + * note: " is not encoded! + * @param string + * @return string + */ + final public static function escapeHtml($s) + { + return str_replace(array('&', '<', '>'), array('&', '<', '>'), $s); + } + + + + /** + * Texy! version of html_entity_decode (always UTF-8, much faster than original!). + * @param string + * @return string + */ + final public static function unescapeHtml($s) + { + if (strpos($s, '&') === FALSE) return $s; + return html_entity_decode($s, ENT_QUOTES, 'UTF-8'); + } + + + + /** + * Outdents text block. + * @param string + * @return string + */ + final public static function outdent($s) + { + $s = trim($s, "\n"); + $spaces = strspn($s, ' '); + if ($spaces) return preg_replace("#^ {1,$spaces}#m", '', $s); + return $s; + } + + + + /** + * Generate unique mark - useful for freezing (folding) some substrings. + * @param string any string to froze + * @param int Texy::CONTENT_* constant + * @return string internal mark + */ + final public function protect($child, $contentType) + { + if ($child==='') return ''; + + $key = $contentType + . strtr(base_convert(count($this->marks), 10, 8), '01234567', "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F") + . $contentType; + + $this->marks[$key] = $child; + + return $key; + } + + + + final public function unProtect($html) + { + return strtr($html, $this->marks); + } + + + + /** + * Filters bad URLs. + * @param string user URL + * @param string type: a-anchor, i-image, c-cite + * @return bool + */ + final public function checkURL($URL, $type) + { + // absolute URL with scheme? check scheme! + if (!empty($this->urlSchemeFilters[$type]) + && preg_match('#'.TEXY_URLSCHEME.'#A', $URL) + && !preg_match($this->urlSchemeFilters[$type], $URL)) + return FALSE; + + return TRUE; + } + + + + /** + * Is given URL relative? + * @param string URL + * @return bool + */ + final public static function isRelative($URL) + { + // check for scheme, or absolute path, or absolute URL + return !preg_match('#'.TEXY_URLSCHEME.'|[\#/?]#A', $URL); + } + + + + /** + * Prepends root to URL, if possible. + * @param string URL + * @param string root + * @return string + */ + final public static function prependRoot($URL, $root) + { + if ($root == NULL || !self::isRelative($URL)) return $URL; + return rtrim($root, '/\\') . '/' . $URL; + } + + + + final public function getLinePatterns() + { + return $this->_linePatterns; + } + + + + final public function getBlockPatterns() + { + return $this->_blockPatterns; + } + + + + final public function getDOM() + { + return $this->DOM; + } + + + + private function tabCb($m) + { + return $m[1] . str_repeat(' ', $this->tabWidth - strlen($m[1]) % $this->tabWidth); + } + + + + /** + * PHP garbage collector helper. + */ + final public function free() + { + if (version_compare(PHP_VERSION , '5.3', '<')) { + foreach (array_keys(get_object_vars($this)) as $key) { + $this->$key = NULL; + } + } + } + + + + final public function __clone() + { + throw new NotSupportedException('Clone is not supported.'); + } + +} diff --git a/apigen/libs/TokenReflection/LICENSE b/apigen/libs/TokenReflection/LICENSE new file mode 100644 index 00000000000..f9030f8e0a5 --- /dev/null +++ b/apigen/libs/TokenReflection/LICENSE @@ -0,0 +1,28 @@ +PHP Token Reflection +Copyright (c) 2011-2012, Ondřej Nešpor, Jaroslav Hanslík. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * The names of authors and contributors may not be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/apigen/libs/TokenReflection/TokenReflection/Broker.php b/apigen/libs/TokenReflection/TokenReflection/Broker.php new file mode 100644 index 00000000000..0276e3fadde --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Broker.php @@ -0,0 +1,542 @@ +cache = array( + self::CACHE_NAMESPACE => array(), + self::CACHE_CLASS => array(), + self::CACHE_CONSTANT => array(), + self::CACHE_FUNCTION => array() + ); + + $this->options = $options; + + $this->backend = $backend + ->setBroker($this) + ->setStoringTokenStreams((bool) ($options & self::OPTION_SAVE_TOKEN_STREAM)); + } + + /** + * Returns broker/parser options. + * + * @return integer + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns if a particular option setting is set. + * + * @param integer $option Option setting + * @return boolean + */ + public function isOptionSet($option) + { + return (bool) ($this->options & $option); + } + + /** + * Parses a string with the PHP source code using the given file name and returns the appropriate reflection object. + * + * @param string $source PHP source code + * @param string $fileName Used file name + * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) + * @return boolean|\TokenReflection\ReflectionFile + */ + public function processString($source, $fileName, $returnReflectionFile = false) + { + if ($this->backend->isFileProcessed($fileName)) { + $tokens = $this->backend->getFileTokens($fileName); + } else { + $tokens = new Stream\StringStream($source, $fileName); + } + + $reflectionFile = new ReflectionFile($tokens, $this); + if (!$this->backend->isFileProcessed($fileName)) { + $this->backend->addFile($tokens, $reflectionFile); + + // Clear the cache - leave only tokenized reflections + foreach ($this->cache as $type => $cached) { + if (!empty($cached)) { + $this->cache[$type] = array_filter($cached, function(IReflection $reflection) { + return $reflection->isTokenized(); + }); + } + } + } + + return $returnReflectionFile ? $reflectionFile : true; + } + + /** + * Parses a file and returns the appropriate reflection object. + * + * @param string $fileName Filename + * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) + * @return boolean|\TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\BrokerException If the file could not be processed. + */ + public function processFile($fileName, $returnReflectionFile = false) + { + try { + if ($this->backend->isFileProcessed($fileName)) { + $tokens = $this->backend->getFileTokens($fileName); + } else { + $tokens = new Stream\FileStream($fileName); + } + + $reflectionFile = new ReflectionFile($tokens, $this); + if (!$this->backend->isFileProcessed($fileName)) { + $this->backend->addFile($tokens, $reflectionFile); + + // Clear the cache - leave only tokenized reflections + foreach ($this->cache as $type => $cached) { + if (!empty($cached)) { + $this->cache[$type] = array_filter($cached, function(IReflection $reflection) { + return $reflection->isTokenized(); + }); + } + } + } + + return $returnReflectionFile ? $reflectionFile : true; + } catch (Exception\ParseException $e) { + throw $e; + } catch (Exception\StreamException $e) { + throw new Exception\BrokerException($this, 'Could not process the file.', 0, $e); + } + } + + /** + * Processes a PHAR archive. + * + * @param string $fileName Archive filename. + * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) + * @return boolean|array of \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\BrokerException If the PHAR PHP extension is not loaded. + * @throws \TokenReflection\Exception\BrokerException If the given archive could not be read. + * @throws \TokenReflection\Exception\BrokerException If the given archive could not be processed. + */ + public function processPhar($fileName, $returnReflectionFile = false) + { + if (!is_file($fileName)) { + throw new Exception\BrokerException($this, 'File does not exist.', Exception\BrokerException::DOES_NOT_EXIST); + } + + if (!extension_loaded('Phar')) { + throw new Exception\BrokerException($this, 'The PHAR PHP extension is not loaded.', Exception\BrokerException::PHP_EXT_MISSING); + } + + try { + $result = array(); + foreach (new RecursiveIteratorIterator(new \Phar($fileName)) as $entry) { + if ($entry->isFile()) { + $result[$entry->getPathName()] = $this->processFile($entry->getPathName(), $returnReflectionFile); + } + } + + return $returnReflectionFile ? $result : true; + } catch (Exception\ParseException $e) { + throw $e; + } catch (Exception\StreamException $e) { + throw new Exception\BrokerException($this, 'Could not process the archive.', 0, $e); + } + } + + /** + * Processes recursively a directory and returns an array of file reflection objects. + * + * @param string $path Directora path + * @param string|array $filters Filename filters + * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) + * @return boolean|array of \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\BrokerException If the given directory does not exist. + * @throws \TokenReflection\Exception\BrokerException If the given directory could not be processed. + */ + public function processDirectory($path, $filters = array(), $returnReflectionFile = false) + { + $realPath = realpath($path); + if (!is_dir($realPath)) { + throw new Exception\BrokerException($this, 'File does not exist.', Exception\BrokerException::DOES_NOT_EXIST); + } + + try { + $result = array(); + foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realPath)) as $entry) { + if ($entry->isFile()) { + $process = empty($filters); + if (!$process) { + foreach ((array) $filters as $filter) { + $whitelisting = '!' !== $filter{0}; + if (fnmatch($whitelisting ? $filter : substr($filter, 1), $entry->getPathName(), FNM_NOESCAPE)) { + $process = $whitelisting; + } + } + } + + if ($process) { + $result[$entry->getPathName()] = $this->processFile($entry->getPathName(), $returnReflectionFile); + } + } + } + + return $returnReflectionFile ? $result : true; + } catch (Exception\ParseException $e) { + throw $e; + } catch (Exception\StreamException $e) { + throw new Exception\BrokerException($this, 'Could not process the directory.', 0, $e); + } + } + + /** + * Process a file, directory or a PHAR archive. + * + * @param string $path Path + * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) + * @return boolean|array|\TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\BrokerException If the target does not exist. + */ + public function process($path, $returnReflectionFile = false) + { + if (is_dir($path)) { + return $this->processDirectory($path, array(), $returnReflectionFile); + } elseif (is_file($path)) { + if (preg_match('~\\.phar(?:$|\\.)~i', $path)) { + return $this->processPhar($path, $returnReflectionFile); + } + + return $this->processFile($path, $returnReflectionFile); + } else { + throw new Exception\BrokerException($this, 'The given directory/file does not exist.', Exception\BrokerException::DOES_NOT_EXIST); + } + } + + /** + * Returns if the broker contains a namespace of the given name. + * + * @param string $namespaceName Namespace name + * @return boolean + */ + public function hasNamespace($namespaceName) + { + return isset($this->cache[self::CACHE_NAMESPACE][$namespaceName]) || $this->backend->hasNamespace($namespaceName); + } + + /** + * Returns a reflection object of the given namespace. + * + * @param string $namespaceName Namespace name + * @return \TokenReflection\ReflectionNamespace|null + */ + public function getNamespace($namespaceName) + { + $namespaceName = ltrim($namespaceName, '\\'); + + if (isset($this->cache[self::CACHE_NAMESPACE][$namespaceName])) { + return $this->cache[self::CACHE_NAMESPACE][$namespaceName]; + } + + $namespace = $this->backend->getNamespace($namespaceName); + if (null !== $namespace) { + $this->cache[self::CACHE_NAMESPACE][$namespaceName] = $namespace; + } + + return $namespace; + } + + /** + * Returns if the broker contains a class of the given name. + * + * @param string $className Class name + * @return boolean + */ + public function hasClass($className) + { + return isset($this->cache[self::CACHE_CLASS][$className]) || $this->backend->hasClass($className); + } + + /** + * Returns a reflection object of the given class (FQN expected). + * + * @param string $className CLass bame + * @return \TokenReflection\ReflectionClass|null + */ + public function getClass($className) + { + $className = ltrim($className, '\\'); + + if (isset($this->cache[self::CACHE_CLASS][$className])) { + return $this->cache[self::CACHE_CLASS][$className]; + } + + $this->cache[self::CACHE_CLASS][$className] = $this->backend->getClass($className); + return $this->cache[self::CACHE_CLASS][$className]; + } + + /** + * Returns all classes from all namespaces. + * + * @param integer $types Returned class types (multiple values may be OR-ed) + * @return array + */ + public function getClasses($types = Broker\Backend::TOKENIZED_CLASSES) + { + return $this->backend->getClasses($types); + } + + /** + * Returns if the broker contains a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasConstant($constantName) + { + return isset($this->cache[self::CACHE_CONSTANT][$constantName]) || $this->backend->hasConstant($constantName); + } + + /** + * Returns a reflection object of a constant (FQN expected). + * + * @param string $constantName Constant name + * @return \TokenReflection\ReflectionConstant|null + */ + public function getConstant($constantName) + { + $constantName = ltrim($constantName, '\\'); + + if (isset($this->cache[self::CACHE_CONSTANT][$constantName])) { + return $this->cache[self::CACHE_CONSTANT][$constantName]; + } + + if ($constant = $this->backend->getConstant($constantName)) { + $this->cache[self::CACHE_CONSTANT][$constantName] = $constant; + } + + return $constant; + } + + /** + * Returns all constants from all namespaces. + * + * @return array + */ + public function getConstants() + { + return $this->backend->getConstants(); + } + + /** + * Returns if the broker contains a function of the given name. + * + * @param string $functionName Function name + * @return boolean + */ + public function hasFunction($functionName) + { + return isset($this->cache[self::CACHE_FUNCTION][$functionName]) || $this->backend->hasFunction($functionName); + } + + /** + * Returns a reflection object of a function (FQN expected). + * + * @param string $functionName Function name + * @return \TokenReflection\ReflectionFunction|null + */ + public function getFunction($functionName) + { + $functionName = ltrim($functionName, '\\'); + + if (isset($this->cache[self::CACHE_FUNCTION][$functionName])) { + return $this->cache[self::CACHE_FUNCTION][$functionName]; + } + + if ($function = $this->backend->getFunction($functionName)) { + $this->cache[self::CACHE_FUNCTION][$functionName] = $function; + } + + return $function; + } + + /** + * Returns all functions from all namespaces. + * + * @return array + */ + public function getFunctions() + { + return $this->backend->getFunctions(); + } + + /** + * Returns if the broker contains a file reflection of the given name. + * + * @param string $fileName File name + * @return boolean + */ + public function hasFile($fileName) + { + return $this->backend->hasFile($fileName); + } + + /** + * Returns a reflection object of a file. + * + * @param string $fileName File name + * @return \TokenReflection\ReflectionFile|null + */ + public function getFile($fileName) + { + return $this->backend->getFile($fileName); + } + + /** + * Returns all processed files reflections. + * + * @return array + */ + public function getFiles() + { + return $this->backend->getFiles(); + } + + /** + * Returns an array of tokens from a processed file. + * + * @param string $fileName File name + * @return \TokenReflection\Stream\StreamBase|null + */ + public function getFileTokens($fileName) + { + return $this->backend->getFileTokens($fileName); + } + + /** + * Returns a real system path. + * + * @param string $path Source path + * @return string|boolean + */ + public static function getRealPath($path) + { + if (0 === strpos($path, 'phar://')) { + return is_file($path) || is_dir($path) ? $path : false; + } else { + return realpath($path); + } + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Broker/Backend.php b/apigen/libs/TokenReflection/TokenReflection/Broker/Backend.php new file mode 100644 index 00000000000..2c36a306d69 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Broker/Backend.php @@ -0,0 +1,212 @@ +files[$fileName]); + } + + /** + * Returns a file reflection. + * + * @param string $fileName File name + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\BrokerException If the requested file has not been processed + */ + public function getFile($fileName) + { + if (!isset($this->files[$fileName])) { + throw new Exception\BrokerException($this->getBroker(), sprintf('File "%s" has not been processed.', $fileName), Exception\BrokerException::DOES_NOT_EXIST); + } + + return $this->files[$fileName]; + } + + /** + * Returns file reflections. + * + * @return array + */ + public function getFiles() + { + return $this->files; + } + + /** + * Returns if there was such namespace processed (FQN expected). + * + * @param string $namespaceName Namespace name + * @return boolean + */ + public function hasNamespace($namespaceName) + { + return isset($this->namespaces[ltrim($namespaceName, '\\')]); + } + + /** + * Returns a reflection object of the given namespace. + * + * @param string $namespaceName Namespace name + * @return \TokenReflection\IReflectionNamespace + * @throws \TokenReflection\Exception\BrokerException If the requested namespace does not exist. + */ + public function getNamespace($namespaceName) + { + if (!isset($this->namespaces[TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME])) { + $this->namespaces[TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME] = new TokenReflection\ReflectionNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME, $this->broker); + } + + $namespaceName = ltrim($namespaceName, '\\'); + if (!isset($this->namespaces[$namespaceName])) { + throw new Exception\BrokerException($this->getBroker(), sprintf('Namespace %s does not exist.', $namespaceName), Exception\BrokerException::DOES_NOT_EXIST); + } + + return $this->namespaces[$namespaceName]; + } + + /** + * Returns all present namespaces. + * + * @return array + */ + public function getNamespaces() + { + return $this->namespaces; + } + + /** + * Returns if there was such class processed (FQN expected). + * + * @param string $className Class name + * @return boolean + */ + public function hasClass($className) + { + $className = ltrim($className, '\\'); + if ($pos = strrpos($className, '\\')) { + $namespace = substr($className, 0, $pos); + + if (!isset($this->namespaces[$namespace])) { + return false; + } + + $namespace = $this->getNamespace($namespace); + $className = substr($className, $pos + 1); + } else { + $namespace = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); + } + + return $namespace->hasClass($className); + } + + /** + * Returns a reflection object of the given class (FQN expected). + * + * @param string $className CLass bame + * @return \TokenReflection\IReflectionClass + */ + public function getClass($className) + { + if (empty($this->declaredClasses)) { + $this->declaredClasses = array_flip(array_merge(get_declared_classes(), get_declared_interfaces())); + } + + $className = ltrim($className, '\\'); + try { + $ns = $this->getNamespace( + ($boundary = strrpos($className, '\\')) + // Class within a namespace + ? substr($className, 0, $boundary) + // Class without a namespace + : TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME + ); + + return $ns->getClass($className); + } catch (Exception\BaseException $e) { + if (isset($this->declaredClasses[$className])) { + $reflection = new Php\ReflectionClass($className, $this->broker); + if ($reflection->isInternal()) { + return $reflection; + } + } + + return new Dummy\ReflectionClass($className, $this->broker); + } + } + + /** + * Returns all classes from all namespaces. + * + * @param integer $type Returned class types (multiple values may be OR-ed) + * @return array + */ + public function getClasses($type = self::TOKENIZED_CLASSES) + { + if (null === $this->allClasses) { + $this->allClasses = $this->parseClassLists(); + } + + $result = array(); + foreach ($this->allClasses as $classType => $classes) { + if ($type & $classType) { + $result = array_merge($result, $classes); + } + } + return $result; + } + + /** + * Returns if there was such constant processed (FQN expected). + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasConstant($constantName) + { + $constantName = ltrim($constantName, '\\'); + + if ($pos = strpos($constantName, '::')) { + $className = substr($constantName, 0, $pos); + $constantName = substr($constantName, $pos + 2); + + if (!$this->hasClass($className)) { + return false; + } + + $parent = $this->getClass($className); + } else { + if ($pos = strrpos($constantName, '\\')) { + $namespace = substr($constantName, 0, $pos); + if (!$this->hasNamespace($namespace)) { + return false; + } + + $parent = $this->getNamespace($namespace); + $constantName = substr($constantName, $pos + 1); + } else { + $parent = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); + } + } + + return $parent->hasConstant($constantName); + } + + /** + * Returns a reflection object of a constant (FQN expected). + * + * @param string $constantName Constant name + * @return \TokenReflection\IReflectionConstant + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstant($constantName) + { + static $declared = array(); + if (empty($declared)) { + $declared = get_defined_constants(); + } + + if ($boundary = strpos($constantName, '::')) { + // Class constant + $className = substr($constantName, 0, $boundary); + $constantName = substr($constantName, $boundary + 2); + + return $this->getClass($className)->getConstantReflection($constantName); + } + + try { + $constantName = ltrim($constantName, '\\'); + if ($boundary = strrpos($constantName, '\\')) { + $ns = $this->getNamespace(substr($constantName, 0, $boundary)); + $constantName = substr($constantName, $boundary + 1); + } else { + $ns = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); + } + + return $ns->getConstant($constantName); + } catch (Exception\BaseException $e) { + if (isset($declared[$constantName])) { + $reflection = new Php\ReflectionConstant($constantName, $declared[$constantName], $this->broker); + if ($reflection->isInternal()) { + return $reflection; + } + } + + throw new Exception\BrokerException($this->getBroker(), sprintf('Constant %s does not exist.', $constantName), Exception\BrokerException::DOES_NOT_EXIST); + } + } + + /** + * Returns all constants from all namespaces. + * + * @return array + */ + public function getConstants() + { + if (null === $this->allConstants) { + $this->allConstants = array(); + foreach ($this->namespaces as $namespace) { + foreach ($namespace->getConstants() as $constant) { + $this->allConstants[$constant->getName()] = $constant; + } + } + } + + return $this->allConstants; + } + + /** + * Returns if there was such function processed (FQN expected). + * + * @param string $functionName Function name + * @return boolean + */ + public function hasFunction($functionName) + { + $functionName = ltrim($functionName, '\\'); + if ($pos = strrpos($functionName, '\\')) { + $namespace = substr($functionName, 0, $pos); + if (!isset($this->namespaces[$namespace])) { + return false; + } + + $namespace = $this->getNamespace($namespace); + $functionName = substr($functionName, $pos + 1); + } else { + $namespace = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); + } + + return $namespace->hasFunction($functionName); + } + + /** + * Returns a reflection object of a function (FQN expected). + * + * @param string $functionName Function name + * @return \TokenReflection\IReflectionFunction + * @throws \TokenReflection\Exception\RuntimeException If the requested function does not exist. + */ + public function getFunction($functionName) + { + static $declared = array(); + if (empty($declared)) { + $functions = get_defined_functions(); + $declared = array_flip($functions['internal']); + } + + $functionName = ltrim($functionName, '\\'); + try { + $ns = $this->getNamespace( + ($boundary = strrpos($functionName, '\\')) + // Function within a namespace + ? substr($functionName, 0, $boundary) + // Function wihout a namespace + : TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME + ); + + return $ns->getFunction($functionName); + } catch (Exception\BaseException $e) { + if (isset($declared[$functionName])) { + return new Php\ReflectionFunction($functionName, $this->broker); + } + + throw new Exception\BrokerException($this->getBroker(), sprintf('Function %s does not exist.', $functionName), Exception\BrokerException::DOES_NOT_EXIST); + } + } + + /** + * Returns all functions from all namespaces. + * + * @return array + */ + public function getFunctions() + { + if (null === $this->allFunctions) { + $this->allFunctions = array(); + foreach ($this->namespaces as $namespace) { + foreach ($namespace->getFunctions() as $function) { + $this->allFunctions[$function->getName()] = $function; + } + } + } + + return $this->allFunctions; + } + + /** + * Returns if the given file was already processed. + * + * @param string $fileName File name + * @return boolean + */ + public function isFileProcessed($fileName) + { + return isset($this->tokenStreams[Broker::getRealPath($fileName)]); + } + + /** + * Returns an array of tokens for a particular file. + * + * @param string $fileName File name + * @return \TokenReflection\Stream\StreamBase + * @throws \TokenReflection\Exception\BrokerException If the requested file was not processed. + */ + public function getFileTokens($fileName) + { + $realName = Broker::getRealPath($fileName); + if (!isset($this->tokenStreams[$realName])) { + throw new Exception\BrokerException($this->getBroker(), sprintf('File "%s" was not processed yet.', $fileName), Exception\BrokerException::DOES_NOT_EXIST); + } + + return true === $this->tokenStreams[$realName] ? new FileStream($realName) : $this->tokenStreams[$realName]; + } + + /** + * Adds a file to the backend storage. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token stream + * @param \TokenReflection\ReflectionFile $file File reflection object + * @return \TokenReflection\Broker\Backend\Memory + */ + public function addFile(TokenReflection\Stream\StreamBase $tokenStream, TokenReflection\ReflectionFile $file) + { + $this->tokenStreams[$file->getName()] = $this->storingTokenStreams ? $tokenStream : true; + $this->files[$file->getName()] = $file; + + $errors = array(); + + foreach ($file->getNamespaces() as $fileNamespace) { + try { + $namespaceName = $fileNamespace->getName(); + if (!isset($this->namespaces[$namespaceName])) { + $this->namespaces[$namespaceName] = new TokenReflection\ReflectionNamespace($namespaceName, $file->getBroker()); + } + + $this->namespaces[$namespaceName]->addFileNamespace($fileNamespace); + } catch (Exception\FileProcessingException $e) { + $errors = array_merge($errors, $e->getReasons()); + } catch (\Exception $e) { + echo $e->getTraceAsString(); + die($e->getMessage()); + } + } + + // Reset all-*-cache + $this->allClasses = null; + $this->allFunctions = null; + $this->allConstants = null; + + if (!empty($errors)) { + throw new Exception\FileProcessingException($errors, $file); + } + + return $this; + } + + /** + * Sets the reflection broker instance. + * + * @param \TokenReflection\Broker $broker Reflection broker + * @return \TokenReflection\Broker\Backend\Memory + */ + public function setBroker(Broker $broker) + { + $this->broker = $broker; + return $this; + } + + /** + * Returns the reflection broker instance. + * + * @return \TokenReflection\Broker $broker Reflection broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Sets if token streams are stored in the backend. + * + * @param boolean $store + * @return \TokenReflection\Broker\Backend + */ + public function setStoringTokenStreams($store) + { + $this->storingTokenStreams = (bool) $store; + return $this; + } + + /** + * Returns if token streams are stored in the backend. + * + * @return boolean + */ + public function getStoringTokenStreams() + { + return $this->storingTokenStreams; + } + + /** + * Prepares and returns used class lists. + * + * @return array + */ + protected function parseClassLists() + { + // Initialize the all-classes-cache + $allClasses = array( + self::TOKENIZED_CLASSES => array(), + self::INTERNAL_CLASSES => array(), + self::NONEXISTENT_CLASSES => array() + ); + + foreach ($this->namespaces as $namespace) { + foreach ($namespace->getClasses() as $class) { + $allClasses[self::TOKENIZED_CLASSES][$class->getName()] = $class; + } + } + + foreach ($allClasses[self::TOKENIZED_CLASSES] as $className => $class) { + foreach (array_merge($class->getParentClasses(), $class->getInterfaces()) as $parent) { + if ($parent->isInternal()) { + $allClasses[self::INTERNAL_CLASSES][$parent->getName()] = $parent; + } elseif (!$parent->isTokenized()) { + $allClasses[self::NONEXISTENT_CLASSES][$parent->getName()] = $parent; + } + } + } + + return $allClasses; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Dummy/ReflectionClass.php b/apigen/libs/TokenReflection/TokenReflection/Dummy/ReflectionClass.php new file mode 100644 index 00000000000..d8322a7e85b --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Dummy/ReflectionClass.php @@ -0,0 +1,1091 @@ +name = ltrim($className, '\\'); + $this->broker = $broker; + } + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? $this->name : substr($this->name, $pos + 1); + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? '' : substr($this->name, 0, $pos); + } + + /** + * Returns if the class is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return false !== strrpos($this->name, '\\'); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns the PHP extension reflection. + * + * @return null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return null + */ + public function getFileName() + { + return null; + } + + /** + * Returns a file reflection. + * + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker + */ + public function getFileReflection() + { + throw new Exception\BrokerException($this->getBroker(), sprintf('Class was not parsed from a file', $this->getName()), Exception\BrokerException::UNSUPPORTED); + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns modifiers. + * + * @return integer + */ + public function getModifiers() + { + return 0; + } + + /** + * Returns if the class is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return false; + } + + /** + * Returns if the class is final. + * + * @return boolean + */ + public function isFinal() + { + return false; + } + + /** + * Returns if the class is an interface. + * + * @return boolean + */ + public function isInterface() + { + return false; + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return false; + } + + /** + * Returns if it is possible to create an instance of this class. + * + * @return boolean + */ + public function isInstantiable() + { + return false; + } + + /** + * Returns traits used by this class. + * + * @return array + */ + public function getTraits() + { + return array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + return array(); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + return array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return array(); + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return array(); + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return false; + } + + /** + * Returns if the class uses a particular trait. + * + * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name + * @return boolean + */ + public function usesTrait($trait) + { + return false; + } + + /** + * Returns if objects of this class are cloneable. + * + * Introduced in PHP 5.4. + * + * @return boolean + * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 + */ + public function isCloneable() + { + return false; + } + + /** + * Returns if the class is iterateable. + * + * Returns true if the class implements the Traversable interface. + * + * @return boolean + */ + public function isIterateable() + { + return false; + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return false; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string|object $class Class name or reflection object + * @return boolean + */ + public function isSubclassOf($class) + { + return false; + } + + /** + * Returns the parent class reflection. + * + * @return null + */ + public function getParentClass() + { + return false; + } + + /** + * Returns the parent classes reflections. + * + * @return array + */ + public function getParentClasses() + { + return array(); + } + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + return array(); + } + + /** + * Returns the parent class reflection. + * + * @return null + */ + public function getParentClassName() + { + return null; + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. + */ + public function implementsInterface($interface) + { + if (is_object($interface)) { + if (!$interface instanceof IReflectionClass) { + throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $interfaceName = $interface->getName(); + + if (!$interface->isInterface()) { + throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + } + + // Only validation, always returns false + return false; + } + + /** + * Returns interface reflections. + * + * @return array + */ + public function getInterfaces() + { + return array(); + } + + /** + * Returns interface names. + * + * @return array + */ + public function getInterfaceNames() + { + return array(); + } + + /** + * Returns interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + return array(); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return array(); + } + + /** + * Returns the class constructor reflection. + * + * @return null + */ + public function getConstructor() + { + return null; + } + + /** + * Returns the class desctructor reflection. + * + * @return null + */ + public function getDestructor() + { + return null; + } + + /** + * Returns if the class implements the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasMethod($name) + { + return false; + } + + /** + * Returns a method reflection. + * + * @param string $name Method name + * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. + */ + public function getMethod($name) + { + throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns method reflections. + * + * @param integer $filter Methods filter + * @return array + */ + public function getMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class implements (and not its parents) the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasOwnMethod($name) + { + return false; + } + + /** + * Returns methods declared by this class, not its parents. + * + * @param integer $filter Methods filter + * @return array + */ + public function getOwnMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class imports the given method from traits. + * + * @param string $name Method name + * @return boolean + */ + public function hasTraitMethod($name) + { + return false; + } + + /** + * Returns method reflections imported from traits. + * + * @param integer $filter Methods filter + * @return array + */ + public function getTraitMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasConstant($name) + { + return false; + } + + /** + * Returns a constant value. + * + * @param string $name Constant name + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstant($name) + { + throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstantReflection($name) + { + throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns an array of constant values. + * + * @return array + */ + public function getConstants() + { + return array(); + } + + /** + * Returns an array of constant reflections. + * + * @return array + */ + public function getConstantReflections() + { + return array(); + } + + /** + * Returns if the class (and not its parents) defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasOwnConstant($name) + { + return false; + } + + /** + * Returns constants declared by this class, not its parents. + * + * @return array + */ + public function getOwnConstants() + { + return array(); + } + + /** + * Returns an array of constant reflections defined by this class not its parents. + * + * @return array + */ + public function getOwnConstantReflections() + { + return array(); + } + + /** + * Returns default properties. + * + * @return array + */ + public function getDefaultProperties() + { + return array(); + } + + /** + * Returns if the class implements the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasProperty($name) + { + return false; + } + + /** + * Returns class properties. + * + * @param integer $filter Property types + * @return array + */ + public function getProperties($filter = null) + { + return array(); + } + + /** + * Return a property reflections. + * + * @param string $name Property name + * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. + */ + public function getProperty($name) + { + throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns if the class (and not its parents) implements the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasOwnProperty($name) + { + return false; + } + + /** + * Returns properties declared by this class, not its parents. + * + * @param integer $filter Properties filter + * @return array + */ + public function getOwnProperties($filter = null) + { + return array(); + } + + /** + * Returns if the class imports the given property from traits. + * + * @param string $name Property name + * @return boolean + */ + public function hasTraitProperty($name) + { + return false; + } + + /** + * Returns property reflections imported from traits. + * + * @param integer $filter Properties filter + * @return array + */ + public function getTraitProperties($filter = null) + { + return array(); + } + + /** + * Returns static properties reflections. + * + * @return array + */ + public function getStaticProperties() + { + return array(); + } + + /** + * Returns a value of a static property. + * + * @param string $name Property name + * @param mixed $default Default value + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + */ + public function getStaticPropertyValue($name, $default = null) + { + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubclasses() + { + return array(); + } + + /** + * Returns names of direct subclasses. + * + * @return array + */ + public function getDirectSubclassNames() + { + return array(); + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclasses() + { + return array(); + } + + /** + * Returns names of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclassNames() + { + return array(); + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + return array(); + } + + /** + * Returns names of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementerNames() + { + return array(); + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + return array(); + } + + /** + * Returns names of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementerNames() + { + return array(); + } + + /** + * Returns if the given object is an instance of this class. + * + * @param object $object Instance + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. + */ + public function isInstance($object) + { + if (!is_object($object)) { + throw new Exception\RuntimeException(sprintf('Parameter must be a class instance, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + return $this->name === get_class($object) || is_subclass_of($object, $this->name); + } + + /** + * Creates a new class instance without using a constructor. + * + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. + */ + public function newInstanceWithoutConstructor() + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new \TokenReflection\Php\ReflectionClass($this->name, $this->getBroker()); + return $reflection->newInstanceWithoutConstructor(); + } + + /** + * Creates a new instance using variable number of parameters. + * + * Use any number of constructor parameters as function parameters. + * + * @param mixed $args + * @return object + */ + public function newInstance($args) + { + return $this->newInstanceArgs(func_get_args()); + } + + /** + * Creates a new instance using an array of parameters. + * + * @param array $args Array of constructor parameters + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. + */ + public function newInstanceArgs(array $args = array()) + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance of class; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new InternalReflectionClass($this->name); + return $reflection->newInstanceArgs($args); + } + + /** + * Sets a static property value. + * + * @param string $name Property name + * @param mixed $value Property value + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + */ + public function setStaticPropertyValue($name, $value) + { + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Class|Interface [ class|interface %s ] {\n %s%s%s%s%s\n}\n", + $this->getName(), + "\n\n - Constants [0] {\n }", + "\n\n - Static properties [0] {\n }", + "\n\n - Static methods [0] {\n }", + "\n\n - Properties [0] {\n }", + "\n\n - Methods [0] {\n }" + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object $className Class name or class instance + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $className, $return = false) + { + TokenReflection\ReflectionClass::export($broker, $className, $return); + } + + /** + * Outputs the reflection subject source code. + * + * @return string + */ + public function getSource() + { + return ''; + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return -1; + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return -1; + } + + /** + * Returns if the class definition is complete. + * + * Dummy classes never have the definition complete. + * + * @return boolean + */ + public function isComplete() + { + return false; + } + + /** + * Returns if the class definition is valid. + * + * Dummy classes are always valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return ReflectionBase::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return ReflectionBase::exists($this, $key); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/BaseException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/BaseException.php new file mode 100644 index 00000000000..aaebfe118f0 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/BaseException.php @@ -0,0 +1,102 @@ +getDetail(); + + return sprintf( + "exception '%s'%s in %s on line %d\n%s\nStack trace:\n%s", + get_class($this), + $this->getMessage() ? " with message '" . $this->getMessage() . "'" : '', + $this->getFile(), + $this->getLine(), + empty($detail) ? '' : $detail . "\n", + $this->getTraceAsString() + ); + } + + /** + * Returns the exception details as string. + * + * @return string + */ + final public function __toString() + { + $output = ''; + + if ($ex = $this->getPrevious()) { + $output .= (string) $ex . "\n\nNext "; + } + + return $output . $this->getOutput() . "\n"; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/BrokerException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/BrokerException.php new file mode 100644 index 00000000000..eb7e21d0c6a --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/BrokerException.php @@ -0,0 +1,66 @@ +broker = $broker; + } + + /** + * Returns the current Broker. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns an exception description detail. + * + * @return string + */ + public function getDetail() + { + return ''; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/FileProcessingException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/FileProcessingException.php new file mode 100644 index 00000000000..48da374153d --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/FileProcessingException.php @@ -0,0 +1,80 @@ +getName()), + 0, + $sender + ); + + $this->reasons = $reasons; + } + + /** + * Returns a list of reasons why the file could not be processed. + * + * @return array + */ + public function getReasons() + { + return $this->reasons; + } + + /** + * Returns an exception description detail. + * + * @return string + */ + public function getDetail() + { + if (!empty($this->reasons)) { + $reasons = array_map(function(BaseException $reason) { + if ($reason instanceof ParseException) { + return $reason->getDetail(); + } else { + return $reason->getMessage(); + } + }, $this->reasons); + + return "There were following reasons for this exception:\n" . implode("\n", $reasons); + } + + return ''; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/ParseException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/ParseException.php new file mode 100644 index 00000000000..c9161c6fdb3 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/ParseException.php @@ -0,0 +1,264 @@ +sender = $sender; + + $token = $tokenStream->current(); + $position = $tokenStream->key(); + + if (!empty($token) && !empty($position)) { + $this->token = $token; + $this->tokenName = $tokenStream->getTokenName(); + + $line = $this->token[2]; + $min = $max = $position; + } else { + $min = $max = $tokenStream->count() - 1; + $line = $tokenStream[$min][2]; + } + + $this->exceptionLine = $line; + + static $skip = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); + + $significant = array(); + while (isset($tokenStream[$min - 1])) { + if (!isset($significant[$tokenStream[$min][2]])) { + if (self::SOURCE_LINES_AROUND <= array_sum($significant)) { + break; + } + + $significant[$tokenStream[$min][2]] = !isset($skip[$tokenStream[$min][0]]); + } else { + $significant[$tokenStream[$min][2]] |= !isset($skip[$tokenStream[$min][0]]); + } + + $min--; + } + + $significant = array(); + while (isset($tokenStream[$max + 1])) { + if (!isset($significant[$tokenStream[$max][2]])) { + if (self::SOURCE_LINES_AROUND <= array_sum($significant)) { + break; + } + + $significant[$tokenStream[$max][2]] = !isset($skip[$tokenStream[$max][0]]); + } else { + $significant[$tokenStream[$max][2]] |= !isset($skip[$tokenStream[$max][0]]); + } + + $max++; + } + + $this->scopeBoundaries = array($min, $max); + } + + /** + * Returns the token where the problem was detected or NULL if the token stream was empty or an end was reached. + * + * @return array|null + */ + public function getToken() + { + return $this->token; + } + + /** + * Returns the name of the token where the problem was detected or NULL if the token stream was empty or an end was reached. + * + * @return string|null + */ + public function getTokenName() + { + return $this->tokenName; + } + + /** + * Returns the line where the exception was thrown. + * + * @return integer + */ + public function getExceptionLine() + { + return $this->exceptionLine; + } + + /** + * Returns the file line with the token or null. + * + * @return integer|null + */ + public function getTokenLine() + { + return null === $this->token ? null : $this->token[2]; + } + + /** + * Returns the source code part around the token. + * + * @param boolean $lineNumbers Returns the source code part with line numbers + * @return string|null + */ + public function getSourcePart($lineNumbers = false) + { + if (empty($this->scopeBoundaries)) { + return null; + } + + list($lo, $hi) = $this->scopeBoundaries; + $stream = $this->getStream(); + + $code = $stream->getSourcePart($lo, $hi); + + if ($lineNumbers) { + $lines = explode("\n", $code); + + $startLine = $stream[$lo][2]; + $width = strlen($startLine + count($lines) - 1); + $errorLine = $this->token[2]; + $actualLine = $startLine; + + $code = implode( + "\n", + array_map(function($line) use (&$actualLine, $width, $errorLine) { + return ($actualLine === $errorLine ? '*' : ' ') . str_pad($actualLine++, $width, ' ', STR_PAD_LEFT) . ': ' . $line; + }, $lines) + ); + } + + return $code; + } + + /** + * Returns the reflection element that caused the exception to be raised. + * + * @return \TokenReflection\IReflection + */ + public function getSender() + { + return $this->sender; + } + + /** + * Returns an exception description detail. + * + * @return string + */ + public function getDetail() + { + if (0 === $this->getStream()->count()) { + return parent::getDetail() . 'The token stream was empty.'; + } elseif (empty($this->token)) { + return parent::getDetail() . 'The token stream was read out of its bounds.'; + } else { + return parent::getDetail() . + sprintf( + "\nThe cause of the exception was the %s token (line %s) in following part of %s source code:\n\n%s", + $this->tokenName, + $this->token[2], + $this->sender && $this->sender->getName() ? $this->sender->getPrettyName() : 'the', + $this->getSourcePart(true) + ); + } + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/RuntimeException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/RuntimeException.php new file mode 100644 index 00000000000..98577794d1f --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/RuntimeException.php @@ -0,0 +1,72 @@ +sender = $sender; + } + + /** + * Returns the reflection element that caused the exception to be raised. + * + * @return \TokenReflection\IReflection + */ + public function getSender() + { + return $this->sender; + } + + /** + * Returns an exception description detail. + * + * @return string + */ + public function getDetail() + { + return null === $this->sender ? '' : sprintf('Thrown when working with "%s".', $this->sender->getPrettyName()); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Exception/StreamException.php b/apigen/libs/TokenReflection/TokenReflection/Exception/StreamException.php new file mode 100644 index 00000000000..5aa7519ea39 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Exception/StreamException.php @@ -0,0 +1,96 @@ +stream = $stream; + } + + /** + * Returns the reflection element that caused the exception to be raised. + * + * @return \TokenReflection\Stream\StreamBase + */ + public function getStream() + { + return $this->stream; + } + + /** + * Returns the processed file name. + * + * @return string + */ + public function getFileName() + { + return $this->stream->getFileName(); + } + + /** + * Returns an exception description detail. + * + * @return string + */ + public function getDetail() + { + return sprintf('Thrown when working with file "%s" token stream.', $this->getFileName()); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/IReflection.php b/apigen/libs/TokenReflection/TokenReflection/IReflection.php new file mode 100644 index 00000000000..911dd633ab0 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/IReflection.php @@ -0,0 +1,80 @@ + 5.3.0, you can uncomment it. + * + * @return mixed + */ + // public function invoke(); + + /** + * Calls the function. + * + * @param array $args Function parameter values + * @return mixed + */ + public function invokeArgs(array $args); + + /** + * Returns the function/method as closure. + * + * @return \Closure + */ + public function getClosure(); + + /** + * Returns if the function definition is valid. + * + * That means that the source code is valid and the function name is unique within parsed files. + * + * @return boolean + */ + public function isValid(); + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases(); +} diff --git a/apigen/libs/TokenReflection/TokenReflection/IReflectionFunctionBase.php b/apigen/libs/TokenReflection/TokenReflection/IReflectionFunctionBase.php new file mode 100644 index 00000000000..9076590cd81 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/IReflectionFunctionBase.php @@ -0,0 +1,135 @@ +name = ltrim($className, '\\'); + $this->fileName = $fileName; + $this->broker = $broker; + } + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? $this->name : substr($this->name, $pos + 1); + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? '' : substr($this->name, 0, $pos); + } + + /** + * Returns if the class is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return false !== strrpos($this->name, '\\'); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns the PHP extension reflection. + * + * @return null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return null + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns a file reflection. + * + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker + */ + public function getFileReflection() + { + throw new Exception\BrokerException($this->getBroker(), sprintf('Class was not parsed from a file', $this->getName()), Exception\BrokerException::UNSUPPORTED); + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns modifiers. + * + * @return integer + */ + public function getModifiers() + { + return 0; + } + + /** + * Returns if the class is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return false; + } + + /** + * Returns if the class is final. + * + * @return boolean + */ + public function isFinal() + { + return false; + } + + /** + * Returns if the class is an interface. + * + * @return boolean + */ + public function isInterface() + { + return false; + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return false; + } + + /** + * Returns if it is possible to create an instance of this class. + * + * @return boolean + */ + public function isInstantiable() + { + return false; + } + + /** + * Returns traits used by this class. + * + * @return array + */ + public function getTraits() + { + return array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + return array(); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + return array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return array(); + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return array(); + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return false; + } + + /** + * Returns if the class uses a particular trait. + * + * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name + * @return boolean + */ + public function usesTrait($trait) + { + return false; + } + + /** + * Returns if objects of this class are cloneable. + * + * Introduced in PHP 5.4. + * + * @return boolean + * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 + */ + public function isCloneable() + { + return false; + } + + /** + * Returns if the class is iterateable. + * + * Returns true if the class implements the Traversable interface. + * + * @return boolean + */ + public function isIterateable() + { + return false; + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string|object $class Class name or reflection object + * @return boolean + */ + public function isSubclassOf($class) + { + return false; + } + + /** + * Returns the parent class reflection. + * + * @return null + */ + public function getParentClass() + { + return false; + } + + /** + * Returns the parent classes reflections. + * + * @return array + */ + public function getParentClasses() + { + return array(); + } + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + return array(); + } + + /** + * Returns the parent class reflection. + * + * @return null + */ + public function getParentClassName() + { + return null; + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. + */ + public function implementsInterface($interface) + { + if (is_object($interface)) { + if (!$interface instanceof IReflectionClass) { + throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $interfaceName = $interface->getName(); + + if (!$interface->isInterface()) { + throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + } + + // Only validation, always returns false + return false; + } + + /** + * Returns interface reflections. + * + * @return array + */ + public function getInterfaces() + { + return array(); + } + + /** + * Returns interface names. + * + * @return array + */ + public function getInterfaceNames() + { + return array(); + } + + /** + * Returns interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + return array(); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return array(); + } + + /** + * Returns the class constructor reflection. + * + * @return null + */ + public function getConstructor() + { + return null; + } + + /** + * Returns the class desctructor reflection. + * + * @return null + */ + public function getDestructor() + { + return null; + } + + /** + * Returns if the class implements the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasMethod($name) + { + return false; + } + + /** + * Returns a method reflection. + * + * @param string $name Method name + * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. + */ + public function getMethod($name) + { + throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns method reflections. + * + * @param integer $filter Methods filter + * @return array + */ + public function getMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class implements (and not its parents) the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasOwnMethod($name) + { + return false; + } + + /** + * Returns methods declared by this class, not its parents. + * + * @param integer $filter Methods filter + * @return array + */ + public function getOwnMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class imports the given method from traits. + * + * @param string $name Method name + * @return boolean + */ + public function hasTraitMethod($name) + { + return false; + } + + /** + * Returns method reflections imported from traits. + * + * @param integer $filter Methods filter + * @return array + */ + public function getTraitMethods($filter = null) + { + return array(); + } + + /** + * Returns if the class defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasConstant($name) + { + return false; + } + + /** + * Returns a constant value. + * + * @param string $name Constant name + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstant($name) + { + throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstantReflection($name) + { + throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns an array of constant values. + * + * @return array + */ + public function getConstants() + { + return array(); + } + + /** + * Returns an array of constant reflections. + * + * @return array + */ + public function getConstantReflections() + { + return array(); + } + + /** + * Returns if the class (and not its parents) defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasOwnConstant($name) + { + return false; + } + + /** + * Returns constants declared by this class, not its parents. + * + * @return array + */ + public function getOwnConstants() + { + return array(); + } + + /** + * Returns an array of constant reflections defined by this class not its parents. + * + * @return array + */ + public function getOwnConstantReflections() + { + return array(); + } + + /** + * Returns default properties. + * + * @return array + */ + public function getDefaultProperties() + { + return array(); + } + + /** + * Returns if the class implements the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasProperty($name) + { + return false; + } + + /** + * Returns class properties. + * + * @param integer $filter Property types + * @return array + */ + public function getProperties($filter = null) + { + return array(); + } + + /** + * Return a property reflections. + * + * @param string $name Property name + * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. + */ + public function getProperty($name) + { + throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns if the class (and not its parents) implements the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasOwnProperty($name) + { + return false; + } + + /** + * Returns properties declared by this class, not its parents. + * + * @param integer $filter Properties filter + * @return array + */ + public function getOwnProperties($filter = null) + { + return array(); + } + + /** + * Returns if the class imports the given property from traits. + * + * @param string $name Property name + * @return boolean + */ + public function hasTraitProperty($name) + { + return false; + } + + /** + * Returns property reflections imported from traits. + * + * @param integer $filter Properties filter + * @return array + */ + public function getTraitProperties($filter = null) + { + return array(); + } + + /** + * Returns static properties reflections. + * + * @return array + */ + public function getStaticProperties() + { + return array(); + } + + /** + * Returns a value of a static property. + * + * @param string $name Property name + * @param mixed $default Default value + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + */ + public function getStaticPropertyValue($name, $default = null) + { + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubclasses() + { + return array(); + } + + /** + * Returns names of direct subclasses. + * + * @return array + */ + public function getDirectSubclassNames() + { + return array(); + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclasses() + { + return array(); + } + + /** + * Returns names of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclassNames() + { + return array(); + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + return array(); + } + + /** + * Returns names of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementerNames() + { + return array(); + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + return array(); + } + + /** + * Returns names of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementerNames() + { + return array(); + } + + /** + * Returns if the given object is an instance of this class. + * + * @param object $object Instance + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. + */ + public function isInstance($object) + { + if (!is_object($object)) { + throw new Exception\RuntimeException(sprintf('Parameter must be a class instance, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + return $this->name === get_class($object) || is_subclass_of($object, $this->name); + } + + /** + * Creates a new class instance without using a constructor. + * + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. + */ + public function newInstanceWithoutConstructor() + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new \TokenReflection\Php\ReflectionClass($this->name, $this->getBroker()); + return $reflection->newInstanceWithoutConstructor(); + } + + /** + * Creates a new instance using variable number of parameters. + * + * Use any number of constructor parameters as function parameters. + * + * @param mixed $args + * @return object + */ + public function newInstance($args) + { + return $this->newInstanceArgs(func_get_args()); + } + + /** + * Creates a new instance using an array of parameters. + * + * @param array $args Array of constructor parameters + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. + */ + public function newInstanceArgs(array $args = array()) + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance of class; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new InternalReflectionClass($this->name); + return $reflection->newInstanceArgs($args); + } + + /** + * Sets a static property value. + * + * @param string $name Property name + * @param mixed $value Property value + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + */ + public function setStaticPropertyValue($name, $value) + { + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Class|Interface [ class|interface %s ] {\n %s%s%s%s%s\n}\n", + $this->getName(), + "\n\n - Constants [0] {\n }", + "\n\n - Static properties [0] {\n }", + "\n\n - Static methods [0] {\n }", + "\n\n - Properties [0] {\n }", + "\n\n - Methods [0] {\n }" + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object $className Class name or class instance + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $className, $return = false) + { + TokenReflection\ReflectionClass::export($broker, $className, $return); + } + + /** + * Outputs the reflection subject source code. + * + * @return string + */ + public function getSource() + { + return ''; + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return -1; + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return -1; + } + + /** + * Returns if the class definition is complete. + * + * Invalid classes are always complete. + * + * @return boolean + */ + public function isComplete() + { + return true; + } + + /** + * Returns if the class definition is valid. + * + * @return boolean + */ + public function isValid() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return ReflectionBase::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return ReflectionBase::exists($this, $key); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionConstant.php b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionConstant.php new file mode 100644 index 00000000000..6fb75bb2028 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionConstant.php @@ -0,0 +1,403 @@ +name = $name; + $this->broker = $broker; + $this->fileName = $fileName; + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? $this->name : substr($this->name, $pos + 1); + } + + /** + * Returns the declaring class reflection. + * + * @return null + */ + public function getDeclaringClass() + { + return null; + } + + /** + * Returns the declaring class name. + * + * @return null + */ + public function getDeclaringClassName() + { + return null; + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? '' : substr($this->name, 0, $pos); + } + + /** + * Returns if the function/method is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return false !== strpos($this->name, '\\'); + } + + /** + * Returns the PHP extension reflection. + * + * @return null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the appropriate source code part. + * + * @return string + */ + public function getSource() + { + return ''; + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return -1; + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return -1; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return null + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns a file reflection. + * + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker + */ + public function getFileReflection() + { + throw new Exception\BrokerException($this->getBroker(), sprintf('Constant %s was not parsed from a file', $this->getPrettyName()), Exception\BrokerException::UNSUPPORTED); + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns the constant value. + * + * @return mixed + */ + public function getValue() + { + return null; + } + + /** + * Returns the part of the source code defining the constant value. + * + * @return string + */ + public function getValueDefinition() + { + return null; + } + + /** + * Returns the originaly provided value definition. + * + * @return string + */ + public function getOriginalValueDefinition() + { + return null; + } + + /** + * Returns if the constant is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the constant is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name; + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Constant [ %s %s ] { %s }\n", + gettype(null), + $this->getName(), + null + ); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns if the constant definition is valid. + * + * @return boolean + */ + public function isValid() + { + return false; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return ReflectionBase::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return ReflectionBase::exists($this, $key); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionElement.php b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionElement.php new file mode 100644 index 00000000000..344d62be16f --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionElement.php @@ -0,0 +1,53 @@ +reasons[] = $reason; + + return $this; + } + + /** + * Returns a list of reasons why this element's reflection is invalid. + * + * @return array + */ + public function getReasons() + { + return $this->reasons; + } + + /** + * Returns if there are any known reasons why this element's reflection is invalid. + * + * @return boolean + */ + public function hasReasons() + { + return !empty($this->reasons); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionFunction.php b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionFunction.php new file mode 100644 index 00000000000..475914b06ed --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Invalid/ReflectionFunction.php @@ -0,0 +1,490 @@ +name = ltrim($name, '\\'); + $this->broker = $broker; + $this->fileName = $fileName; + } + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? $this->name : substr($this->name, $pos + 1); + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + $pos = strrpos($this->name, '\\'); + return false === $pos ? '' : substr($this->name, 0, $pos); + } + + /** + * Returns if the class is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return false !== strrpos($this->name, '\\'); + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name . '()'; + } + + /** + * Returns the PHP extension reflection. + * + * @return \TokenReflection\IReflectionExtension|null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return false + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return null + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns a file reflection. + * + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker + */ + public function getFileReflection() + { + throw new Exception\BrokerException($this->getBroker(), sprintf('Function was not parsed from a file', $this->getPrettyName()), Exception\BrokerException::UNSUPPORTED); + } + + /** + * Returns the appropriate source code part. + * + * @return string + */ + public function getSource() + { + return ''; + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return -1; + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return -1; + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns all annotations. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns if the function/method is a closure. + * + * @return boolean + */ + public function isClosure() + { + return false; + } + + /** + * Returns if the function/method is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return false; + } + + /** + * Returns a function/method parameter. + * + * @param integer|string $parameter Parameter name or position + */ + public function getParameter($parameter) + { + if (is_numeric($parameter)) { + throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } else { + throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } + + /** + * Returns function/method parameters. + * + * @return array + */ + public function getParameters(){ + return array(); + } + + /** + * Returns the number of parameters. + * + * @return integer + */ + public function getNumberOfParameters() + { + return 0; + } + + /** + * Returns the number of required parameters. + * + * @return integer + */ + public function getNumberOfRequiredParameters() + { + return 0; + } + + /** + * Returns static variables. + * + * @return array + */ + public function getStaticVariables() + { + return array(); + } + + /** + * Returns if the method is is disabled via the disable_functions directive. + * + * @return boolean + */ + public function isDisabled() + { + return false; + } + + /** + * Calls the function. + * + * @return mixed + */ + public function invoke() + { + return $this->invokeArgs(array()); + } + + /** + * Calls the function. + * + * @param array $args Function parameter values + * @return mixed + */ + public function invokeArgs(array $args) + { + throw new Exception\RuntimeException('Cannot invoke invalid functions', Exception\RuntimeException::UNSUPPORTED, $this); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns the function/method as closure. + * + * @return \Closure + */ + public function getClosure() + { + throw new Exception\RuntimeException('Cannot invoke invalid functions', Exception\RuntimeException::UNSUPPORTED, $this); + } + + /** + * Returns the closure scope class. + * + * @return null + */ + public function getClosureScopeClass() + { + return null; + } + + /** + * Returns this pointer bound to closure. + * + * @return null + */ + public function getClosureThis() + { + return null; + } + + /** + * Returns if the function definition is valid. + * + * @return boolean + */ + public function isValid() + { + return false; + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "%sFunction [ function %s%s ] {\n @@ %s %d - %d\n}\n", + $this->getDocComment() ? $this->getDocComment() . "\n" : '', + $this->returnsReference() ? '&' : '', + $this->getName(), + $this->getFileName(), + $this->getStartLine(), + $this->getEndLine() + ); + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return ReflectionBase::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return ReflectionBase::exists($this, $key); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/IReflection.php b/apigen/libs/TokenReflection/TokenReflection/Php/IReflection.php new file mode 100644 index 00000000000..ce8353ed3b4 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/IReflection.php @@ -0,0 +1,36 @@ +broker = $broker; + } + + /** + * Returns the PHP extension reflection. + * + * @return \TokenReflection\Php\ReflectionExtension + */ + public function getExtension() + { + return ReflectionExtension::create(parent::getExtension(), $this->broker); + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return 'Exception' === $this->getName() || $this->isSubclassOf('Exception'); + } + + /** + * Returns if objects of this class are cloneable. + * + * Introduced in PHP 5.4. + * + * @return boolean + * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 + */ + public function isCloneable() + { + if ($this->isInterface() || $this->isAbstract()) { + return false; + } + + $methods = $this->getMethods(); + return isset($methods['__clone']) ? $methods['__clone']->isPublic() : true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string|object $class Class name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. + */ + public function isSubclassOf($class) + { + if (is_object($class)) { + if (!$class instanceof InternalReflectionClass && !$class instanceof IReflectionClass) { + throw new Exception\RuntimeException('Parameter must be a string or an instance of class reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $class = $class->getName(); + } + + return in_array($class, $this->getParentClassNameList()); + } + + /** + * Returns parent class reflection. + * + * @return \TokenReflection\Php\ReflectionClass + */ + public function getParentClass() + { + $parent = parent::getParentClass(); + return $parent ? self::create($parent, $this->broker) : null; + } + + /** + * Returns the parent class name. + * + * @return string + */ + public function getParentClassName() + { + $parent = $this->getParentClass(); + return $parent ? $parent->getName() : null; + } + + /** + * Returns the parent classes reflections. + * + * @return array + */ + public function getParentClasses() + { + $broker = $this->broker; + return array_map(function($className) use ($broker) { + return $broker->getClass($className); + }, $this->getParentClassNameList()); + } + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + return class_parents($this->getName()); + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. + */ + public function implementsInterface($interface) + { + if (is_object($interface)) { + if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) { + throw new Exception\RuntimeException('Parameter must be a string or an instance of class reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $interfaceName = $interface->getName(); + + if (!$interface->isInterface()) { + throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + } else { + $reflection = $this->getBroker()->getClass($interface); + if (!$reflection->isInterface()) { + throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interface), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $interfaceName = $interface; + } + + $interfaces = $this->getInterfaces(); + return isset($interfaces[$interfaceName]); + } + + /** + * Returns an array of interface reflections. + * + * @return array + */ + public function getInterfaces() + { + if (null === $this->interfaces) { + $broker = $this->broker; + $interfaceNames = $this->getInterfaceNames(); + + if (empty($interfaceNames)) { + $this->interfaces = array(); + } else { + $this->interfaces = array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { + return $broker->getClass($interfaceName); + }, $interfaceNames)); + } + } + + return $this->interfaces; + } + + /** + * Returns interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + $parent = $this->getParentClass(); + return $parent ? array_diff_key($this->getInterfaces(), $parent->getInterfaces()) : $this->getInterfaces(); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return array_keys($this->getOwnInterfaces()); + } + + /** + * Returns class constructor reflection. + * + * @return \TokenReflection\Php\ReflectionClass|null + */ + public function getConstructor() + { + return ReflectionMethod::create(parent::getConstructor(), $this->broker); + } + + /** + * Returns class desctructor reflection. + * + * @return \TokenReflection\Php\ReflectionClass|null + */ + public function getDestructor() + { + foreach ($this->getMethods() as $method) { + if ($method->isDestructor()) { + return $method; + } + } + + return null; + } + + /** + * Returns a particular method reflection. + * + * @param string $name Method name + * @return \TokenReflection\Php\ReflectionMethod + * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. + */ + public function getMethod($name) + { + foreach ($this->getMethods() as $method) { + if ($method->getName() === $name) { + return $method; + } + } + + throw new Exception\RuntimeException(sprintf('Method %s does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns class methods. + * + * @param integer $filter Methods filter + * @return array + */ + public function getMethods($filter = null) + { + if (null === $this->methods) { + $broker = $this->broker; + $this->methods = array_map(function(InternalReflectionMethod $method) use ($broker) { + return ReflectionMethod::create($method, $broker); + }, parent::getMethods()); + } + + if (null === $filter) { + return $this->methods; + } + + return array_filter($this->methods, function(ReflectionMethod $method) use ($filter) { + return (bool) ($method->getModifiers() & $filter); + }); + } + + /** + * Returns if the class implements (and not its parents) the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasOwnMethod($name) + { + foreach ($this->getOwnMethods() as $method) { + if ($name === $method->getName()) { + return true; + } + } + + return false; + } + + /** + * Returns methods declared by this class, not its parents. + * + * @param integer $filter + * @return array + */ + public function getOwnMethods($filter = null) + { + $me = $this->getName(); + return array_filter($this->getMethods($filter), function(ReflectionMethod $method) use ($me) { + return $method->getDeclaringClass()->getName() === $me; + }); + } + + /** + * Returns if the class imports the given method from traits. + * + * @param string $name Method name + * @return boolean + * @todo Impossible with the current status of reflection + */ + public function hasTraitMethod($name) + { + return false; + } + + /** + * Returns method reflections imported from traits. + * + * @param integer $filter Methods filter + * @return array + * @todo Impossible with the current status of reflection + */ + public function getTraitMethods($filter = null) + { + return array(); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \TokenReflection\ReflectionConstant + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstantReflection($name) + { + if ($this->hasConstant($name)) { + return new ReflectionConstant($name, $this->getConstant($name), $this->broker, $this); + } + + throw new Exception\RuntimeException(sprintf('Constant "%s" does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns an array of constant reflections. + * + * @return array + */ + public function getConstantReflections() + { + if (null === $this->constants) { + $this->constants = array(); + foreach ($this->getConstants() as $name => $value) { + $this->constants[$name] = $this->getConstantReflection($name); + } + } + + return array_values($this->constants); + } + + /** + * Returns if the class (and not its parents) defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasOwnConstant($name) + { + $constants = $this->getOwnConstants(); + return isset($constants[$name]); + } + + /** + * Returns constants declared by this class, not its parents. + * + * @return array + */ + public function getOwnConstants() + { + return array_diff_assoc($this->getConstants(), $this->getParentClass() ? $this->getParentClass()->getConstants() : array()); + } + + /** + * Returns an array of constant reflections defined by this class and not its parents. + * + * @return array + */ + public function getOwnConstantReflections() + { + $constants = array(); + foreach ($this->getOwnConstants() as $name => $value) { + $constants[] = $this->getConstantReflection($name); + } + return $constants; + } + + /** + * Returns a particular property reflection. + * + * @param string $name Property name + * @return \TokenReflection\Php\ReflectionProperty + * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. + */ + public function getProperty($name) + { + foreach ($this->getProperties() as $property) { + if ($name === $property->getName()) { + return $property; + } + } + + throw new Exception\RuntimeException(sprintf('Property %s does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns class properties. + * + * @param integer $filter Properties filter + * @return array + */ + public function getProperties($filter = null) + { + if (null === $this->properties) { + $broker = $this->broker; + $this->properties = array_map(function(InternalReflectionProperty $property) use ($broker) { + return ReflectionProperty::create($property, $broker); + }, parent::getProperties()); + } + + if (null === $filter) { + return $this->properties; + } + + return array_filter($this->properties, function(ReflectionProperty $property) use ($filter) { + return (bool) ($property->getModifiers() & $filter); + }); + } + + /** + * Returns if the class has (and not its parents) the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasOwnProperty($name) + { + foreach ($this->getOwnProperties() as $property) { + if ($name === $property->getName()) { + return true; + } + } + + return false; + } + + /** + * Returns properties declared by this class, not its parents. + * + * @param integer $filter + * @return array + */ + public function getOwnProperties($filter = null) + { + $me = $this->getName(); + return array_filter($this->getProperties($filter), function(ReflectionProperty $property) use ($me) { + return $property->getDeclaringClass()->getName() === $me; + }); + } + + /** + * Returns if the class imports the given property from traits. + * + * @param string $name Property name + * @return boolean + * @todo Impossible with the current status of reflection + */ + public function hasTraitProperty($name) + { + return false; + } + + /** + * Returns property reflections imported from traits. + * + * @param integer $filter Properties filter + * @return array + * @todo Impossible with the current status of reflection + */ + public function getTraitProperties($filter = null) + { + return array(); + } + + /** + * Returns static properties reflections. + * + * @return array + */ + public function getStaticProperties() + { + return $this->getProperties(InternalReflectionProperty::IS_STATIC); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubclasses() + { + $that = $this->name; + return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { + if (!$class->isSubclassOf($that)) { + return false; + } + + return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that); + }); + } + + /** + * Returns names of direct subclasses. + * + * @return array + */ + public function getDirectSubclassNames() + { + return array_keys($this->getDirectSubclasses()); + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclasses() + { + $that = $this->name; + return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { + if (!$class->isSubclassOf($that)) { + return false; + } + + return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that); + }); + } + + /** + * Returns names of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclassNames() + { + return array_keys($this->getIndirectSubclasses()); + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $that = $this->name; + return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { + if (!$class->implementsInterface($that)) { + return false; + } + + return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that); + }); + } + + /** + * Returns names of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementerNames() + { + return array_keys($this->getDirectImplementers()); + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $that = $this->name; + return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { + if (!$class->implementsInterface($that)) { + return false; + } + + return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that); + }); + } + + /** + * Returns names of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementerNames() + { + return array_keys($this->getIndirectImplementers()); + } + + /** + * Returns if the class definition is complete. + * + * Internal classes always have the definition complete. + * + * @return boolean + */ + public function isComplete() + { + return true; + } + + /** + * Returns if the class definition is valid. + * + * Internal classes are always valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Returns traits used by this class. + * + * @return array + */ + public function getTraits() + { + return NATIVE_TRAITS ? parent::getTraits() : array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + if (!NATIVE_TRAITS) { + return array(); + } + + $parent = $this->getParentClass(); + return $parent ? array_diff_key($this->getTraits(), $parent->getTraits()) : $this->getTraits(); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + return NATIVE_TRAITS ? parent::getTraitNames() : array(); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return array_keys($this->getOwnTraits()); + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return NATIVE_TRAITS ? parent::getTraitAliases() : array(); + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return NATIVE_TRAITS && parent::isTrait(); + } + + /** + * Returns if the class uses a particular trait. + * + * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. + */ + public function usesTrait($trait) + { + if (is_object($trait)) { + if (!$trait instanceof InternalReflectionClass && !$trait instanceof TokenReflection\IReflectionClass) { + throw new Exception\RuntimeException('Parameter must be a string or an instance of trait reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $traitName = $trait->getName(); + + if (!$trait->isTrait()) { + throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + } else { + $reflection = $this->getBroker()->getClass($trait); + if (!$reflection->isTrait()) { + throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $traitName = $trait; + } + + return in_array($traitName, $this->getTraitNames()); + } + + /** + * Creates a new class instance without using a constructor. + * + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. + */ + public function newInstanceWithoutConstructor() + { + if ($this->isInternal()) { + throw new Exception\RuntimeException('Could not create an instance; only user defined classes can be instantiated.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + foreach ($this->getParentClasses() as $parent) { + if ($parent->isInternal()) { + throw new Exception\RuntimeException('Could not create an instance; only user defined classes can be instantiated.', Exception\RuntimeException::UNSUPPORTED, $this); + } + } + + if (PHP_VERSION_ID >= 50400) { + return parent::newInstanceWithoutConstructor(); + } + + return unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->getName()), $this->getName())); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->getName(); + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\ReflectionClass + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + if (!$internalReflection instanceof InternalReflectionClass) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionClass expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + return $broker->getClass($internalReflection->getName()); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionConstant.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionConstant.php new file mode 100644 index 00000000000..e0de08fa562 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionConstant.php @@ -0,0 +1,486 @@ +name = $name; + $this->value = $value; + $this->broker = $broker; + + if (null !== $parent) { + $realParent = null; + + if (array_key_exists($name, $parent->getOwnConstants())) { + $realParent = $parent; + } + + if (null === $realParent) { + foreach ($parent->getParentClasses() as $grandParent) { + if (array_key_exists($name, $grandParent->getOwnConstants())) { + $realParent = $grandParent; + break; + } + } + } + + if (null === $realParent) { + foreach ($parent->getInterfaces() as $interface) { + if (array_key_exists($name, $interface->getOwnConstants())) { + $realParent = $interface; + break; + } + } + } + + if (null === $realParent) { + throw new Exception\RuntimeException('Could not determine constant real parent class.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $this->declaringClassName = $realParent->getName(); + $this->userDefined = $realParent->isUserDefined(); + } else { + if (!array_key_exists($name, get_defined_constants(false))) { + $this->userDefined = true; + } else { + $declared = get_defined_constants(true); + $this->userDefined = array_key_exists($name, $declared['user']); + } + } + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $name = $this->getName(); + if (null !== $this->namespaceName && $this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { + $name = substr($name, strlen($this->namespaceName) + 1); + } + + return $name; + } + + /** + * Returns the declaring class reflection. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getDeclaringClass() + { + if (null === $this->declaringClassName) { + return null; + } + + return $this->getBroker()->getClass($this->declaringClassName); + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClassName; + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->namespaceName === TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; + } + + /** + * Returns if the function/method is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return '' !== $this->getNamespaceName(); + } + + /** + * Returns the PHP extension reflection. + * + * @return null + */ + public function getExtension() + { + // @todo + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return null + */ + public function getFileName() + { + return null; + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns the constant value. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the part of the source code defining the constant value. + * + * @return string + */ + public function getValueDefinition() + { + return var_export($this->value, true); + } + + /** + * Returns the originaly provided value definition. + * + * @return string + */ + public function getOriginalValueDefinition() + { + return token_get_all($this->getValueDefinition()); + } + + /** + * Returns if the constant is internal. + * + * @return boolean + */ + public function isInternal() + { + return !$this->userDefined; + } + + /** + * Returns if the constant is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return $this->userDefined; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return null === $this->declaringClassName ? $this->name : sprintf('%s::%s', $this->declaringClassName, $this->name); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Constant [ %s %s ] { %s }\n", + gettype($this->getValue()), + $this->getName(), + $this->getValue() + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object|null $class Class name, class instance or null + * @param string $constant Constant name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $class, $constant, $return = false) + { + $className = is_object($class) ? get_class($class) : $class; + $constantName = $constant; + + if (null === $className) { + try { + $constant = $broker->getConstant($constantName); + } catch (Exception\BrokerException $e) { + throw new Exception\RuntimeException(sprintf('Constant %s does not exist.', $constantName), Exception\RuntimeException::DOES_NOT_EXIST); + } + } else { + $class = $broker->getClass($className); + if ($class instanceof Invalid\ReflectionClass) { + throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); + } elseif ($class instanceof Dummy\ReflectionClass) { + throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); + } + $constant = $class->getConstantReflection($constantName); + } + + if ($return) { + return $constant->__toString(); + } + + echo $constant->__toString(); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns if the constant definition is valid. + * + * Internal constants are always valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Creates a reflection instance. + * + * Not supported for constants since there is no internal constant reflection. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return null + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + return null; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionExtension.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionExtension.php new file mode 100644 index 00000000000..20bbe7b96f9 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionExtension.php @@ -0,0 +1,282 @@ +broker = $broker; + } + + /** + * Returns if the constant is internal. + * + * @return boolean + */ + public function isInternal() + { + return true; + } + + /** + * Returns if the constant is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return false; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns a class reflection. + * + * @param string $name Class name + * @return \TokenReflection\IReflectionClass|null + */ + public function getClass($name) + { + $classes = $this->getClasses(); + return isset($classes[$name]) ? $classes[$name] : null; + } + + /** + * Returns classes defined by this extension. + * + * @return array + */ + public function getClasses() + { + if (null === $this->classes) { + $broker = $this->broker; + $this->classes = array_map(function($className) use ($broker) { + return $broker->getClass($className); + }, $this->getClassNames()); + } + + return $this->classes; + } + + /** + * Returns a constant value. + * + * @param string $name Constant name + * @return mixed|false + */ + public function getConstant($name) + { + $constants = $this->getConstants(); + return isset($constants[$name]) ? $constants[$name] : false; + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \TokenReflection\IReflectionConstant + */ + public function getConstantReflection($name) + { + $constants = $this->getConstantReflections(); + return isset($constants[$name]) ? $constants[$name] : null; + } + + /** + * Returns reflections of defined constants. + * + * @return array + */ + public function getConstantReflections() + { + if (null === $this->constants) { + $broker = $this->broker; + $this->constants = array_map(function($constantName) use ($broker) { + return $broker->getConstant($constantName); + }, array_keys($this->getConstants())); + } + + return $this->constants; + } + + /** + * Returns a function reflection. + * + * @param string $name Function name + * @return \TokenReflection\IReflectionFunction + */ + public function getFunction($name) + { + $functions = $this->getFunctions(); + return isset($functions[$name]) ? $functions[$name] : null; + } + + /** + * Returns functions defined by this extension. + * + * @return array + */ + public function getFunctions() + { + if (null === $this->functions) { + $broker = $this->broker; + $this->classes = array_map(function($functionName) use ($broker) { + return $broker->getFunction($functionName); + }, array_keys(parent::getFunctions())); + } + + return $this->functions; + } + + /** + * Returns names of functions defined by this extension. + * + * @return array + */ + public function getFunctionNames() + { + return array_keys($this->getFunctions()); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->getName(); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\ReflectionExtension + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + static $cache = array(); + + if (!$internalReflection instanceof InternalReflectionExtension) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionExtension expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + if (!isset($cache[$internalReflection->getName()])) { + $cache[$internalReflection->getName()] = new self($internalReflection->getName(), $broker); + } + + return $cache[$internalReflection->getName()]; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionFunction.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionFunction.php new file mode 100644 index 00000000000..63e47373496 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionFunction.php @@ -0,0 +1,271 @@ +broker = $broker; + } + + /** + * Returns the PHP extension reflection. + * + * @return \TokenReflection\IReflectionExtension + */ + public function getExtension() + { + return ReflectionExtension::create(parent::getExtension(), $this->broker); + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns a particular parameter. + * + * @param integer|string $parameter Parameter name or position + * @return \TokenReflection\Php\ReflectionParameter + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. + */ + public function getParameter($parameter) + { + $parameters = $this->getParameters(); + + if (is_numeric($parameter)) { + if (!isset($parameters[$parameter])) { + throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $parameters[$parameter]; + } else { + foreach ($parameters as $reflection) { + if ($reflection->getName() === $parameter) { + return $reflection; + } + } + + throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } + + /** + * Returns function parameters. + * + * @return array + */ + public function getParameters() + { + if (null === $this->parameters) { + $broker = $this->broker; + $parent = $this; + $this->parameters = array_map(function(InternalReflectionParameter $parameter) use ($broker, $parent) { + return ReflectionParameter::create($parameter, $broker, $parent); + }, parent::getParameters()); + } + + return $this->parameters; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Returns the function/method as closure. + * + * @return \Closure + */ + public function getClosure() + { + if (PHP_VERSION >= 50400) { + return parent::getClosure(); + } else { + $that = $this; + return function() use ($that) { + return $that->invokeArgs(func_get_args()); + }; + } + } + + /** + * Returns the closure scope class. + * + * @return string|null + */ + public function getClosureScopeClass() + { + return PHP_VERSION >= 50400 ? parent::getClosureScopeClass() : null; + } + + /** + * Returns this pointer bound to closure. + * + * @return null + */ + public function getClosureThis() + { + return PHP_VERSION >= 50400 ? parent::getClosureThis() : null; + } + + /** + * Returns if the function definition is valid. + * + * Internal functions are always valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->getName() . '()'; + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\ReflectionFunction + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + if (!$internalReflection instanceof InternalReflectionFunction) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionFunction expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + return $broker->getFunction($internalReflection->getName()); + } +} \ No newline at end of file diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionMethod.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionMethod.php new file mode 100644 index 00000000000..7a0c90ff71e --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionMethod.php @@ -0,0 +1,385 @@ +broker = $broker; + } + + /** + * Returns the declaring class reflection. + * + * @return \TokenReflection\IReflectionClass + */ + public function getDeclaringClass() + { + return ReflectionClass::create(parent::getDeclaringClass(), $this->broker); + } + + /** + * Returns the declaring class name. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->getDeclaringClass()->getName(); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->getDeclaringClass()->getNamespaceAliases(); + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns the method prototype. + * + * @return \TokenReflection\Php\ReflectionMethod + */ + public function getPrototype() + { + return self::create(parent::getPrototype(), $this->broker); + } + + /** + * Returns a particular parameter. + * + * @param integer|string $parameter Parameter name or position + * @return \TokenReflection\Php\ReflectionParameter + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. + */ + public function getParameter($parameter) + { + $parameters = $this->getParameters(); + + if (is_numeric($parameter)) { + if (!isset($parameters[$parameter])) { + throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $parameters[$parameter]; + } else { + foreach ($parameters as $reflection) { + if ($reflection->getName() === $parameter) { + return $reflection; + } + } + + throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } + + /** + * Returns function parameters. + * + * @return array + */ + public function getParameters() + { + if (null === $this->parameters) { + $broker = $this->broker; + $parent = $this; + $this->parameters = array_map(function(InternalReflectionParameter $parameter) use ($broker, $parent) { + return ReflectionParameter::create($parameter, $broker, $parent); + }, parent::getParameters()); + } + + return $this->parameters; + } + + /** + * Returns if the method is set accessible. + * + * @return boolean + */ + public function isAccessible() + { + return $this->accessible; + } + + /** + * Sets a method to be accessible or not. + * + * Introduced in PHP 5.3.2. Throws an exception if run on an older version. + * + * @param boolean $accessible + * @throws \TokenReflection\Exception\RuntimeException If run on PHP version < 5.3.2. + */ + public function setAccessible($accessible) + { + if (PHP_VERSION_ID < 50302) { + throw new Exception\RuntimeException(sprintf('Method setAccessible was introduced the internal reflection in PHP 5.3.2, you are using %s.', PHP_VERSION), Exception\RuntimeException::UNSUPPORTED, $this); + } + + $this->accessible = $accessible; + + parent::setAccessible($accessible); + } + + /** + * Shortcut for isPublic(), ... methods that allows or-ed modifiers. + * + * @param integer $filter Filter + * @return boolean + */ + public function is($filter = null) + { + return null === $filter || ($this->getModifiers() & $filter); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Returns the function/method as closure. + * + * @param object $object Object + * @return \Closure + */ + public function getClosure($object) + { + if (PHP_VERSION >= 50400) { + return parent::getClosure(); + } else { + $that = $this; + return function() use ($object, $that) { + return $that->invokeArgs($object, func_get_args()); + }; + } + } + + /** + * Returns the closure scope class. + * + * @return string|null + */ + public function getClosureScopeClass() + { + return PHP_VERSION >= 50400 ? parent::getClosureScopeClass() : null; + } + + /** + * Returns this pointer bound to closure. + * + * @return null + */ + public function getClosureThis() + { + return PHP_VERSION >= 50400 ? parent::getClosureThis() : null; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string + */ + public function getOriginalName() + { + return null; + } + + /** + * Returns the original method when importing from a trait. + * + * @return null + */ + public function getOriginal() + { + return null; + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return null + */ + public function getOriginalModifiers() + { + return null; + } + + /** + * Returns the defining trait. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getDeclaringTrait() + { + return null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return null; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::%s()', $this->getDeclaringClassName(), $this->getName()); + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\IReflection + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + static $cache = array(); + + if (!$internalReflection instanceof InternalReflectionMethod) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionMethod expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + $key = $internalReflection->getDeclaringClass()->getName() . '::' . $internalReflection->getName(); + if (!isset($cache[$key])) { + $cache[$key] = new self($internalReflection->getDeclaringClass()->getName(), $internalReflection->getName(), $broker); + } + + return $cache[$key]; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionParameter.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionParameter.php new file mode 100644 index 00000000000..35cd7bba614 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionParameter.php @@ -0,0 +1,368 @@ +broker = $broker; + $this->userDefined = $parent->isUserDefined(); + } + + /** + * Returns the declaring class reflection. + * + * @return \TokenReflection\IReflectionClass + */ + public function getDeclaringClass() + { + $class = parent::getDeclaringClass(); + return $class ? ReflectionClass::create($class, $this->broker) : null; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + $class = parent::getDeclaringClass(); + return $class ? $class->getName() : null; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->getDeclaringFunction()->getNamespaceAliases(); + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->getDeclaringFunction()->getFileName(); + } + + /** + * Returns the PHP extension reflection. + * + * @return \TokenReflection\Php\ReflectionExtension + */ + public function getExtension() + { + return $this->getDeclaringFunction()->getExtension(); + } + + /** + * Returns the PHP extension name. + * + * @return string|boolean + */ + public function getExtensionName() + { + $extension = $this->getExtension(); + return $extension ? $extension->getName() : false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns the declaring function reflection. + * + * @return \TokenReflection\Php\ReflectionFunction|\TokenReflection\Php\ReflectionMethod + */ + public function getDeclaringFunction() + { + $class = $this->getDeclaringClass(); + $function = parent::getDeclaringFunction(); + + return $class ? $class->getMethod($function->getName()) : ReflectionFunction::create($function, $this->broker); + } + + /** + * Returns the declaring function name. + * + * @return string|null + */ + public function getDeclaringFunctionName() + { + $function = parent::getDeclaringFunction(); + return $function ? $function->getName() : $function; + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Returns the part of the source code defining the paramter default value. + * + * @return string|null + */ + public function getDefaultValueDefinition() + { + $value = $this->getDefaultValue(); + return null === $value ? null : var_export($value, true); + } + + /** + * Returns if the parameter expects a callback. + * + * @return boolean + */ + public function isCallable() + { + return PHP_VERSION >= 50400 && parent::isCallable(); + } + + /** + * Returns the original type hint as defined in the source code. + * + * @return string|null + */ + public function getOriginalTypeHint() + { + return !$this->isArray() && !$this->isCallable() ? $this->getClass() : null; + } + + /** + * Returns the required class name of the value. + * + * @return string|null + */ + public function getClassName() + { + return $this->getClass() ? $this->getClass()->getName() : null; + } + + /** + * Returns if the parameter is internal. + * + * @return boolean + */ + public function isInternal() + { + return !$this->userDefined; + } + + /** + * Returns if the parameter is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return $this->userDefined; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns if the paramter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return method_exists($this, 'canBePassedByValue') ? parent::canBePassedByValue() : !$this->isPassedByReference(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return str_replace('()', '($' . $this->getName() . ')', $this->getDeclaringFunction()->getPrettyName()); + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\ReflectionParameter + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + static $cache = array(); + + if (!$internalReflection instanceof InternalReflectionParameter) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionParameter expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + $class = $internalReflection->getDeclaringClass(); + $function = $internalReflection->getDeclaringFunction(); + + $key = $class ? $class->getName() . '::' : ''; + $key .= $function->getName() . '(' . $internalReflection->getName() . ')'; + + if (!isset($cache[$key])) { + $cache[$key] = new self($class ? array($class->getName(), $function->getName()) : $function->getName(), $internalReflection->getName(), $broker, $function); + } + + return $cache[$key]; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionProperty.php b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionProperty.php new file mode 100644 index 00000000000..fc0c192da06 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Php/ReflectionProperty.php @@ -0,0 +1,348 @@ +broker = $broker; + } + + /** + * Returns the declaring class reflection. + * + * @return \TokenReflection\IReflectionClass + */ + public function getDeclaringClass() + { + return ReflectionClass::create(parent::getDeclaringClass(), $this->broker); + } + + /** + * Returns the declaring class name. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->getDeclaringClass()->getName(); + } + + /** + * Returns the definition start line number in the file. + * + * @return null + */ + public function getStartLine() + { + return null; + } + + /** + * Returns the definition end line number in the file. + * + * @return null + */ + public function getEndLine() + { + return null; + } + + /** + * Returns the appropriate docblock definition. + * + * @return boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + return false; + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return null + */ + public function getAnnotation($name) + { + return null; + } + + /** + * Returns parsed docblock. + * + * @return array + */ + public function getAnnotations() + { + return array(); + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + $values = $this->getDeclaringClass()->getDefaultProperties(); + return $values[$this->getName()]; + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + $value = $this->getDefaultValue(); + return null === $value ? null : var_export($value, true); + } + + /** + * Returns if the property is internal. + * + * @return boolean + */ + public function isInternal() + { + return $this->getDeclaringClass()->isInternal(); + } + + /** + * Returns if the property is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return $this->getDeclaringClass()->isUserDefined(); + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return false; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return false; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return array(); + } + + /** + * Returns the defining trait. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getDeclaringTrait() + { + return null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return null; + } + + /** + * Returns if the property is set accessible. + * + * @return boolean + */ + public function isAccessible() + { + return $this->accessible; + } + + /** + * Sets a property to be accessible or not. + * + * @param boolean $accessible If the property should be accessible. + */ + public function setAccessible($accessible) + { + $this->accessible = (bool) $accessible; + + parent::setAccessible($accessible); + } + + /** + * Returns the PHP extension reflection. + * + * @return \TokenReflection\Php\ReflectionExtension + */ + public function getExtension() + { + return $this->getDeclaringClass()->getExtension(); + } + + /** + * Returns the PHP extension name. + * + * @return string|boolean + */ + public function getExtensionName() + { + $extension = $this->getExtension(); + return $extension ? $extension->getName() : false; + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->getDeclaringClass()->getFileName(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::$%s', $this->getDeclaringClassName(), $this->getName()); + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return TokenReflection\ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return TokenReflection\ReflectionElement::exists($this, $key); + } + + /** + * Creates a reflection instance. + * + * @param \ReflectionClass $internalReflection Internal reflection instance + * @param \TokenReflection\Broker $broker Reflection broker instance + * @return \TokenReflection\Php\ReflectionProperty + * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. + */ + public static function create(Reflector $internalReflection, Broker $broker) + { + static $cache = array(); + + if (!$internalReflection instanceof InternalReflectionProperty) { + throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionProperty expected.', Exception\RuntimeException::INVALID_ARGUMENT); + } + + $key = $internalReflection->getDeclaringClass()->getName() . '::' . $internalReflection->getName(); + if (!isset($cache[$key])) { + $cache[$key] = new self($internalReflection->getDeclaringClass()->getName(), $internalReflection->getName(), $broker); + } + + return $cache[$key]; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionAnnotation.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionAnnotation.php new file mode 100644 index 00000000000..6cd8def2fdc --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionAnnotation.php @@ -0,0 +1,484 @@ +reflection = $reflection; + $this->docComment = $docComment ?: false; + } + + /** + * Returns the docblock. + * + * @return string|boolean + */ + public function getDocComment() + { + return $this->docComment; + } + + /** + * Returns if the current docblock contains the requrested annotation. + * + * @param string $annotation Annotation name + * @return boolean + */ + public function hasAnnotation($annotation) + { + if (null === $this->annotations) { + $this->parse(); + } + + return isset($this->annotations[$annotation]); + } + + /** + * Returns a particular annotation value. + * + * @param string $annotation Annotation name + * @return string|array|null + */ + public function getAnnotation($annotation) + { + if (null === $this->annotations) { + $this->parse(); + } + + return isset($this->annotations[$annotation]) ? $this->annotations[$annotation] : null; + } + + /** + * Returns all parsed annotations. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + $this->parse(); + } + + return $this->annotations; + } + + /** + * Sets Docblock templates. + * + * @param array $templates Docblock templates + * @return \TokenReflection\ReflectionAnnotation + * @throws \TokenReflection\Exception\RuntimeException If an invalid annotation template was provided. + */ + public function setTemplates(array $templates) + { + foreach ($templates as $template) { + if (!$template instanceof ReflectionAnnotation) { + throw new Exception\RuntimeException( + sprintf( + 'All templates have to be instances of \\TokenReflection\\ReflectionAnnotation; %s given.', + is_object($template) ? get_class($template) : gettype($template) + ), + Exception\RuntimeException::INVALID_ARGUMENT, + $this->reflection + ); + } + } + + $this->templates = $templates; + + return $this; + } + + /** + * Parses reflection object documentation. + */ + private function parse() + { + $this->annotations = array(); + + if (false !== $this->docComment) { + // Parse docblock + $name = self::SHORT_DESCRIPTION; + $docblock = trim( + preg_replace( + array( + '~^' . preg_quote(ReflectionElement::DOCBLOCK_TEMPLATE_START, '~') . '~', + '~^' . preg_quote(ReflectionElement::DOCBLOCK_TEMPLATE_END, '~') . '$~', + '~^/\\*\\*~', + '~\\*/$~' + ), + '', + $this->docComment + ) + ); + foreach (explode("\n", $docblock) as $line) { + $line = preg_replace('~^\\*\\s?~', '', trim($line)); + + // End of short description + if ('' === $line && self::SHORT_DESCRIPTION === $name) { + $name = self::LONG_DESCRIPTION; + continue; + } + + // @annotation + if (preg_match('~^\\s*@([\\S]+)\\s*(.*)~', $line, $matches)) { + $name = $matches[1]; + $this->annotations[$name][] = $matches[2]; + continue; + } + + // Continuation + if (self::SHORT_DESCRIPTION === $name || self::LONG_DESCRIPTION === $name) { + if (!isset($this->annotations[$name])) { + $this->annotations[$name] = $line; + } else { + $this->annotations[$name] .= "\n" . $line; + } + } else { + $this->annotations[$name][count($this->annotations[$name]) - 1] .= "\n" . $line; + } + } + + array_walk_recursive($this->annotations, function(&$value) { + // {@*} is a placeholder for */ (phpDocumentor compatibility) + $value = str_replace('{@*}', '*/', $value); + $value = trim($value); + }); + } + + if ($this->reflection instanceof ReflectionElement) { + // Merge docblock templates + $this->mergeTemplates(); + + // Copy annotations if the @copydoc tag is present. + if (!empty($this->annotations['copydoc'])) { + $this->copyAnnotation(); + } + + // Process docblock inheritance for supported reflections + if ($this->reflection instanceof ReflectionClass || $this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty) { + $this->inheritAnnotations(); + } + } + } + + /** + * Copies annotations if the @copydoc tag is present. + * + * @throws \TokenReflection\Exception\RuntimeException When stuck in an infinite loop when resolving the @copydoc tag. + */ + private function copyAnnotation() + { + self::$copydocStack[] = $this->reflection; + $broker = $this->reflection->getBroker(); + + $parentNames = $this->annotations['copydoc']; + unset($this->annotations['copydoc']); + + foreach ($parentNames as $parentName) { + try { + if ($this->reflection instanceof ReflectionClass) { + $parent = $broker->getClass($parentName); + if ($parent instanceof Dummy\ReflectionClass) { + // The class to copy from is not usable + return; + } + } elseif ($this->reflection instanceof ReflectionFunction) { + $parent = $broker->getFunction(rtrim($parentName, '()')); + } elseif ($this->reflection instanceof ReflectionConstant && null === $this->reflection->getDeclaringClassName()) { + $parent = $broker->getConstant($parentName); + } elseif ($this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty || $this->reflection instanceof ReflectionConstant) { + if (false !== strpos($parentName, '::')) { + list($className, $parentName) = explode('::', $parentName, 2); + $class = $broker->getClass($className); + } else { + $class = $this->reflection->getDeclaringClass(); + } + + if ($class instanceof Dummy\ReflectionClass) { + // The source element class is not usable + return; + } + + if ($this->reflection instanceof ReflectionMethod) { + $parent = $class->getMethod(rtrim($parentName, '()')); + } elseif ($this->reflection instanceof ReflectionConstant) { + $parent = $class->getConstantReflection($parentName); + } else { + $parent = $class->getProperty(ltrim($parentName, '$')); + } + } + + if (!empty($parent)) { + // Don't get into an infinite recursion loop + if (in_array($parent, self::$copydocStack, true)) { + throw new Exception\RuntimeException('Infinite loop detected when copying annotations using the @copydoc tag.', Exception\RuntimeException::INVALID_ARGUMENT, $this->reflection); + } + + self::$copydocStack[] = $parent; + + // We can get into an infinite loop here (e.g. when two methods @copydoc from each other) + foreach ($parent->getAnnotations() as $name => $value) { + // Add annotations that are not already present + if (empty($this->annotations[$name])) { + $this->annotations[$name] = $value; + } + } + + array_pop(self::$copydocStack); + } + } catch (Exception\BaseException $e) { + // Ignoring links to non existent elements, ... + } + } + + array_pop(self::$copydocStack); + } + + /** + * Merges templates with the current docblock. + */ + private function mergeTemplates() + { + foreach ($this->templates as $index => $template) { + if (0 === $index && $template->getDocComment() === $this->docComment) { + continue; + } + + foreach ($template->getAnnotations() as $name => $value) { + if ($name === self::LONG_DESCRIPTION) { + // Long description + if (isset($this->annotations[self::LONG_DESCRIPTION])) { + $this->annotations[self::LONG_DESCRIPTION] = $value . "\n" . $this->annotations[self::LONG_DESCRIPTION]; + } else { + $this->annotations[self::LONG_DESCRIPTION] = $value; + } + } elseif ($name !== self::SHORT_DESCRIPTION) { + // Tags; short description is not inherited + if (isset($this->annotations[$name])) { + $this->annotations[$name] = array_merge($this->annotations[$name], $value); + } else { + $this->annotations[$name] = $value; + } + } + } + } + } + + /** + * Inherits annotations from parent classes/methods/properties if needed. + * + * @throws \TokenReflection\Exception\RuntimeException If unsupported reflection was used. + */ + private function inheritAnnotations() + { + if ($this->reflection instanceof ReflectionClass) { + $declaringClass = $this->reflection; + } elseif ($this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty) { + $declaringClass = $this->reflection->getDeclaringClass(); + } + + $parents = array_filter(array_merge(array($declaringClass->getParentClass()), $declaringClass->getOwnInterfaces()), function($class) { + return $class instanceof ReflectionClass; + }); + + // In case of properties and methods, look for a property/method of the same name and return + // and array of such members. + $parentDefinitions = array(); + if ($this->reflection instanceof ReflectionProperty) { + $name = $this->reflection->getName(); + foreach ($parents as $parent) { + if ($parent->hasProperty($name)) { + $parentDefinitions[] = $parent->getProperty($name); + } + } + + $parents = $parentDefinitions; + } elseif ($this->reflection instanceof ReflectionMethod) { + $name = $this->reflection->getName(); + foreach ($parents as $parent) { + if ($parent->hasMethod($name)) { + $parentDefinitions[] = $parent->getMethod($name); + } + } + + $parents = $parentDefinitions; + } + + if (false === $this->docComment) { + // Inherit the entire docblock + foreach ($parents as $parent) { + $annotations = $parent->getAnnotations(); + if (!empty($annotations)) { + $this->annotations = $annotations; + break; + } + } + } else { + if (isset($this->annotations[self::LONG_DESCRIPTION]) && false !== stripos($this->annotations[self::LONG_DESCRIPTION], '{@inheritdoc}')) { + // Inherit long description + foreach ($parents as $parent) { + if ($parent->hasAnnotation(self::LONG_DESCRIPTION)) { + $this->annotations[self::LONG_DESCRIPTION] = str_ireplace( + '{@inheritdoc}', + $parent->getAnnotation(self::LONG_DESCRIPTION), + $this->annotations[self::LONG_DESCRIPTION] + ); + break; + } + } + + $this->annotations[self::LONG_DESCRIPTION] = str_ireplace('{@inheritdoc}', '', $this->annotations[self::LONG_DESCRIPTION]); + } + if (isset($this->annotations[self::SHORT_DESCRIPTION]) && false !== stripos($this->annotations[self::SHORT_DESCRIPTION], '{@inheritdoc}')) { + // Inherit short description + foreach ($parents as $parent) { + if ($parent->hasAnnotation(self::SHORT_DESCRIPTION)) { + $this->annotations[self::SHORT_DESCRIPTION] = str_ireplace( + '{@inheritdoc}', + $parent->getAnnotation(self::SHORT_DESCRIPTION), + $this->annotations[self::SHORT_DESCRIPTION] + ); + break; + } + } + + $this->annotations[self::SHORT_DESCRIPTION] = str_ireplace('{@inheritdoc}', '', $this->annotations[self::SHORT_DESCRIPTION]); + } + } + + // In case of properties check if we need and can inherit the data type + if ($this->reflection instanceof ReflectionProperty && empty($this->annotations['var'])) { + foreach ($parents as $parent) { + if ($parent->hasAnnotation('var')) { + $this->annotations['var'] = $parent->getAnnotation('var'); + break; + } + } + } + + if ($this->reflection instanceof ReflectionMethod) { + if (0 !== $this->reflection->getNumberOfParameters() && (empty($this->annotations['param']) || count($this->annotations['param']) < $this->reflection->getNumberOfParameters())) { + // In case of methods check if we need and can inherit parameter descriptions + $params = isset($this->annotations['param']) ? $this->annotations['param'] : array(); + $complete = false; + foreach ($parents as $parent) { + if ($parent->hasAnnotation('param')) { + $parentParams = array_slice($parent->getAnnotation('param'), count($params)); + + while (!empty($parentParams) && !$complete) { + array_push($params, array_shift($parentParams)); + + if (count($params) === $this->reflection->getNumberOfParameters()) { + $complete = true; + } + } + } + + if ($complete) { + break; + } + } + + if (!empty($params)) { + $this->annotations['param'] = $params; + } + } + + // And check if we need and can inherit the return and throws value + foreach (array('return', 'throws') as $paramName) { + if (!isset($this->annotations[$paramName])) { + foreach ($parents as $parent) { + if ($parent->hasAnnotation($paramName)) { + $this->annotations[$paramName] = $parent->getAnnotation($paramName); + break; + } + } + } + } + } + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionBase.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionBase.php new file mode 100644 index 00000000000..c7b15b0a399 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionBase.php @@ -0,0 +1,273 @@ +broker = $broker; + + $this->parseStream($tokenStream, $parent); + } + + /** + * Parses the token substream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + */ + abstract protected function parseStream(Stream $tokenStream, IReflection $parent = null); + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + return $this->docComment->getDocComment(); + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + final public function hasAnnotation($name) + { + return $this->docComment->hasAnnotation($name); + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + final public function getAnnotation($name) + { + return $this->docComment->getAnnotation($name); + } + + /** + * Returns all annotations. + * + * @return array + */ + final public function getAnnotations() + { + return $this->docComment->getAnnotations(); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Returns if the reflection object is internal. + * + * Always returns false - everything is user defined. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the reflection object is user defined. + * + * Always returns true - everything is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns if the reflection subject is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return $this->hasAnnotation('deprecated'); + } + + /** + * Returns the appropriate source code part. + * + * @return string + */ + abstract public function getSource(); + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return self::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return self::exists($this, $key); + } + + /** + * Magic __get method helper. + * + * @param \TokenReflection\IReflection $object Reflection object + * @param string $key Variable name + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If the requested parameter does not exist. + */ + final public static function get(IReflection $object, $key) + { + if (!empty($key)) { + $className = get_class($object); + if (!isset(self::$methodCache[$className])) { + self::$methodCache[$className] = array_flip(get_class_methods($className)); + } + + $methods = self::$methodCache[$className]; + $key2 = ucfirst($key); + if (isset($methods['get' . $key2])) { + return $object->{'get' . $key2}(); + } elseif (isset($methods['is' . $key2])) { + return $object->{'is' . $key2}(); + } + } + + throw new Exception\RuntimeException(sprintf('Cannot read property "%s".', $key), Exception\RuntimeException::DOES_NOT_EXIST); + } + + /** + * Magic __isset method helper. + * + * @param \TokenReflection\IReflection $object Reflection object + * @param string $key Variable name + * @return boolean + */ + final public static function exists(IReflection $object, $key) + { + try { + self::get($object, $key); + return true; + } catch (RuntimeException $e) { + return false; + } + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionClass.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionClass.php new file mode 100644 index 00000000000..088ebca0b0b --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionClass.php @@ -0,0 +1,1986 @@ +::] => array( + * array(, [])|null + * [, ...] + * ) + * + * @var array + */ + private $traitImports = array(); + + /** + * Stores if the class definition is complete. + * + * @var array + */ + private $methods = array(); + + /** + * Constant reflections. + * + * @var array + */ + private $constants = array(); + + /** + * Properties reflections. + * + * @var array + */ + private $properties = array(); + + /** + * Stores if the class definition is complete. + * + * @var boolean + */ + private $definitionComplete = false; + + /** + * Imported namespace/class aliases. + * + * @var array + */ + private $aliases = array(); + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + $name = $this->getName(); + if ($this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { + $name = substr($name, strlen($this->namespaceName) + 1); + } + + return $name; + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; + } + + /** + * Returns if the class is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName; + } + + /** + * Returns modifiers. + * + * @return array + */ + public function getModifiers() + { + if (false === $this->modifiersComplete) { + if (($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) && !($this->modifiers & InternalReflectionClass::IS_IMPLICIT_ABSTRACT)) { + foreach ($this->getMethods() as $reflectionMethod) { + if ($reflectionMethod->isAbstract()) { + $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; + } + } + + if (!empty($this->interfaces)) { + $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; + } + } + + if (!empty($this->interfaces)) { + $this->modifiers |= self::IMPLEMENTS_INTERFACES; + } + + if ($this->isInterface() && !empty($this->methods)) { + $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; + } + + if (!empty($this->traits)) { + $this->modifiers |= self::IMPLEMENTS_TRAITS; + } + + $this->modifiersComplete = null === $this->parentClassName || $this->getParentClass()->isComplete(); + + if ($this->modifiersComplete) { + foreach ($this->getInterfaces() as $interface) { + if (!$interface->isComplete()) { + $this->modifiersComplete = false; + break; + } + } + } + if ($this->modifiersComplete) { + foreach ($this->getTraits() as $trait) { + if (!$trait->isComplete()) { + $this->modifiersComplete = false; + break; + } + } + } + } + + return $this->modifiers; + } + + /** + * Returns if the class is abstract. + * + * @return boolean + */ + public function isAbstract() + { + if ($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) { + return true; + } elseif ($this->isInterface() && !empty($this->methods)) { + return true; + } + + return false; + } + + /** + * Returns if the class is final. + * + * @return boolean + */ + public function isFinal() + { + return (bool) ($this->modifiers & InternalReflectionClass::IS_FINAL); + } + + /** + * Returns if the class is an interface. + * + * @return boolean + */ + public function isInterface() + { + return (bool) ($this->modifiers & self::IS_INTERFACE); + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return 'Exception' === $this->name || $this->isSubclassOf('Exception'); + } + + /** + * Returns if it is possible to create an instance of this class. + * + * @return boolean + */ + public function isInstantiable() + { + if ($this->isInterface() || $this->isAbstract()) { + return false; + } + + if (null === ($constructor = $this->getConstructor())) { + return true; + } + + return $constructor->isPublic(); + } + + /** + * Returns if objects of this class are cloneable. + * + * Introduced in PHP 5.4. + * + * @return boolean + * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 + */ + public function isCloneable() + { + if ($this->isInterface() || $this->isAbstract()) { + return false; + } + + if ($this->hasMethod('__clone')) { + return $this->getMethod('__clone')->isPublic(); + } + + return true; + } + + /** + * Returns if the class is iterateable. + * + * Returns true if the class implements the Traversable interface. + * + * @return boolean + * @todo traits + */ + public function isIterateable() + { + return $this->implementsInterface('Traversable'); + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string|object $class Class name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not a reflection class instance. + */ + public function isSubclassOf($class) + { + if (is_object($class)) { + if ($class instanceof InternalReflectionClass || $class instanceof IReflectionClass) { + $class = $class->getName(); + } else { + $class = get_class($class); + } + } + + if ($class === $this->parentClassName) { + return true; + } + + $parent = $this->getParentClass(); + return false === $parent ? false : $parent->isSubclassOf($class); + } + + /** + * Returns the parent class reflection. + * + * @return \TokenReflection\ReflectionClass|boolean + */ + public function getParentClass() + { + $className = $this->getParentClassName(); + if (null === $className) { + return false; + } + + return $this->getBroker()->getClass($className); + } + + /** + * Returns the parent class name. + * + * @return string|null + */ + public function getParentClassName() + { + return $this->parentClassName; + } + + /** + * Returns the parent classes reflections. + * + * @return array + */ + public function getParentClasses() + { + $parent = $this->getParentClass(); + if (false === $parent) { + return array(); + } + + return array_merge(array($parent->getName() => $parent), $parent->getParentClasses()); + } + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + $parent = $this->getParentClass(); + if (false === $parent) { + return array(); + } + + return array_merge(array($parent->getName()), $parent->getParentClassNameList()); + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. + */ + public function implementsInterface($interface) + { + if (is_object($interface)) { + if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) { + throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + if (!$interface->isInterface()) { + throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $interfaceName = $interface->getName(); + } else { + $interfaceName = $interface; + } + + return in_array($interfaceName, $this->getInterfaceNames()); + } + + /** + * Returns interface reflections. + * + * @return array + */ + public function getInterfaces() + { + $interfaceNames = $this->getInterfaceNames(); + if (empty($interfaceNames)) { + return array(); + } + + $broker = $this->getBroker(); + return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { + return $broker->getClass($interfaceName); + }, $interfaceNames)); + } + + /** + * Returns interface names. + * + * @return array + */ + public function getInterfaceNames() + { + $parentClass = $this->getParentClass(); + + $names = false !== $parentClass ? array_reverse(array_flip($parentClass->getInterfaceNames())) : array(); + foreach ($this->interfaces as $interfaceName) { + $names[$interfaceName] = true; + foreach (array_reverse($this->getBroker()->getClass($interfaceName)->getInterfaceNames()) as $parentInterfaceName) { + $names[$parentInterfaceName] = true; + } + } + + return array_keys($names); + } + + /** + * Returns reflections of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + $interfaceNames = $this->getOwnInterfaceNames(); + if (empty($interfaceNames)) { + return array(); + } + + $broker = $this->getBroker(); + return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { + return $broker->getClass($interfaceName); + }, $interfaceNames)); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return $this->interfaces; + } + + /** + * Returns the class constructor reflection. + * + * @return \TokenReflection\ReflectionMethod|null + */ + public function getConstructor() + { + foreach ($this->getMethods() as $method) { + if ($method->isConstructor()) { + return $method; + } + } + + return null; + } + + /** + * Returns the class destructor reflection. + * + * @return \TokenReflection\ReflectionMethod|null + */ + public function getDestructor() + { + foreach ($this->getMethods() as $method) { + if ($method->isDestructor()) { + return $method; + } + } + + return null; + } + + /** + * Returns if the class implements the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasMethod($name) + { + foreach ($this->getMethods() as $method) { + if ($name === $method->getName()) { + return true; + } + } + + return false; + } + + /** + * Returns a method reflection. + * + * @param string $name Method name + * @return \TokenReflection\ReflectionMethod + * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. + */ + public function getMethod($name) + { + if (isset($this->methods[$name])) { + return $this->methods[$name]; + } + + foreach ($this->getMethods() as $method) { + if ($name === $method->getName()) { + return $method; + } + } + + throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns method reflections. + * + * @param integer $filter Methods filter + * @return array + */ + public function getMethods($filter = null) + { + $methods = $this->methods; + + foreach ($this->getTraitMethods() as $traitMethod) { + if (!isset($methods[$traitMethod->getName()])) { + $methods[$traitMethod->getName()] = $traitMethod; + } + } + + if (null !== $this->parentClassName) { + foreach ($this->getParentClass()->getMethods(null) as $parentMethod) { + if (!isset($methods[$parentMethod->getName()])) { + $methods[$parentMethod->getName()] = $parentMethod; + } + } + } + foreach ($this->getOwnInterfaces() as $interface) { + foreach ($interface->getMethods(null) as $parentMethod) { + if (!isset($methods[$parentMethod->getName()])) { + $methods[$parentMethod->getName()] = $parentMethod; + } + } + } + + if (null !== $filter) { + $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) { + return $method->is($filter); + }); + } + + return array_values($methods); + } + + /** + * Returns if the class implements (and not its parents) the given method. + * + * @param string $name Method name + * @return boolean + */ + public function hasOwnMethod($name) + { + return isset($this->methods[$name]); + } + + /** + * Returns reflections of methods declared by this class, not its parents. + * + * @param integer $filter Methods filter + * @return array + */ + public function getOwnMethods($filter = null) + { + $methods = $this->methods; + + if (null !== $filter) { + $methods = array_filter($methods, function(ReflectionMethod $method) use ($filter) { + return $method->is($filter); + }); + } + + return array_values($methods); + } + + /** + * Returns if the class imports the given method from traits. + * + * @param string $name Method name + * @return boolean + */ + public function hasTraitMethod($name) + { + if (isset($this->methods[$name])) { + return false; + } + + foreach ($this->getOwnTraits() as $trait) { + if ($trait->hasMethod($name)) { + return true; + } + } + + return false; + } + + /** + * Returns reflections of method imported from traits. + * + * @param integer $filter Methods filter + * @return array + * @throws \TokenReflection\Exception\RuntimeException If trait method was already imported. + */ + public function getTraitMethods($filter = null) + { + $methods = array(); + + foreach ($this->getOwnTraits() as $trait) { + $traitName = $trait->getName(); + foreach ($trait->getMethods(null) as $traitMethod) { + $methodName = $traitMethod->getName(); + + $imports = array(); + if (isset($this->traitImports[$traitName . '::' . $methodName])) { + $imports = $this->traitImports[$traitName . '::' . $methodName]; + } + if (isset($this->traitImports[$methodName])) { + $imports = empty($imports) ? $this->traitImports[$methodName] : array_merge($imports, $this->traitImports[$methodName]); + } + + foreach ($imports as $import) { + if (null !== $import) { + list($newName, $accessLevel) = $import; + + if ('' === $newName) { + $newName = $methodName; + $imports[] = null; + } + + if (!isset($this->methods[$newName])) { + if (isset($methods[$newName])) { + throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $newName), Exception\RuntimeException::ALREADY_EXISTS, $this); + } + + $methods[$newName] = $traitMethod->alias($this, $newName, $accessLevel); + } + } + } + + if (!in_array(null, $imports)) { + if (!isset($this->methods[$methodName])) { + if (isset($methods[$methodName])) { + throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $methodName), Exception\RuntimeException::ALREADY_EXISTS, $this); + } + + $methods[$methodName] = $traitMethod->alias($this); + } + } + } + } + + if (null !== $filter) { + $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) { + return (bool) ($method->getModifiers() & $filter); + }); + } + + return array_values($methods); + } + + /** + * Returns if the class defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasConstant($name) + { + if (isset($this->constants[$name])) { + return true; + } + + foreach ($this->getConstantReflections() as $constant) { + if ($name === $constant->getName()) { + return true; + } + } + + return false; + } + + /** + * Returns a constant value. + * + * @param string $name Constant name + * @return mixed|false + */ + public function getConstant($name) + { + try { + return $this->getConstantReflection($name)->getValue(); + } catch (Exception\BaseException $e) { + return false; + } + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \TokenReflection\ReflectionConstant + * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. + */ + public function getConstantReflection($name) + { + if (isset($this->constants[$name])) { + return $this->constants[$name]; + } + + foreach ($this->getConstantReflections() as $constant) { + if ($name === $constant->getName()) { + return $constant; + } + } + + throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns constant values. + * + * @return array + */ + public function getConstants() + { + $constants = array(); + foreach ($this->getConstantReflections() as $constant) { + $constants[$constant->getName()] = $constant->getValue(); + } + return $constants; + } + + /** + * Returns constant reflections. + * + * @return array + */ + public function getConstantReflections() + { + if (null === $this->parentClassName && empty($this->interfaces)) { + return array_values($this->constants); + } else { + $reflections = array_values($this->constants); + + if (null !== $this->parentClassName) { + $reflections = array_merge($reflections, $this->getParentClass()->getConstantReflections()); + } + foreach ($this->getOwnInterfaces() as $interface) { + $reflections = array_merge($reflections, $interface->getConstantReflections()); + } + + return $reflections; + } + } + + /** + * Returns if the class (and not its parents) defines the given constant. + * + * @param string $name Constant name. + * @return boolean + */ + public function hasOwnConstant($name) + { + return isset($this->constants[$name]); + } + + /** + * Returns constants declared by this class, not by its parents. + * + * @return array + */ + public function getOwnConstants() + { + return array_map(function(ReflectionConstant $constant) { + return $constant->getValue(); + }, $this->constants); + } + + /** + * Returns reflections of constants declared by this class, not by its parents. + * + * @return array + */ + public function getOwnConstantReflections() + { + return array_values($this->constants); + } + + /** + * Returns if the class defines the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasProperty($name) + { + foreach ($this->getProperties() as $property) { + if ($name === $property->getName()) { + return true; + } + } + + return false; + } + + /** + * Return a property reflection. + * + * @param string $name Property name + * @return \TokenReflection\ReflectionProperty + * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. + */ + public function getProperty($name) + { + if (isset($this->properties[$name])) { + return $this->properties[$name]; + } + + foreach ($this->getProperties() as $property) { + if ($name === $property->getName()) { + return $property; + } + } + + throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name, $this->name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns property reflections. + * + * @param integer $filter Properties filter + * @return array + */ + public function getProperties($filter = null) + { + $properties = $this->properties; + + foreach ($this->getTraitProperties(null) as $traitProperty) { + if (!isset($properties[$traitProperty->getName()])) { + $properties[$traitProperty->getName()] = $traitProperty->alias($this); + } + } + + if (null !== $this->parentClassName) { + foreach ($this->getParentClass()->getProperties(null) as $parentProperty) { + if (!isset($properties[$parentProperty->getName()])) { + $properties[$parentProperty->getName()] = $parentProperty; + } + } + } + + if (null !== $filter) { + $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) { + return (bool) ($property->getModifiers() & $filter); + }); + } + + return array_values($properties); + } + + /** + * Returns if the class (and not its parents) defines the given property. + * + * @param string $name Property name + * @return boolean + */ + public function hasOwnProperty($name) + { + return isset($this->properties[$name]); + } + + /** + * Returns reflections of properties declared by this class, not its parents. + * + * @param integer $filter Properties filter + * @return array + */ + public function getOwnProperties($filter = null) + { + $properties = $this->properties; + + if (null !== $filter) { + $properties = array_filter($properties, function(ReflectionProperty $property) use ($filter) { + return (bool) ($property->getModifiers() & $filter); + }); + } + + return array_values($properties); + } + + /** + * Returns if the class imports the given property from traits. + * + * @param string $name Property name + * @return boolean + */ + public function hasTraitProperty($name) + { + if (isset($this->properties[$name])) { + return false; + } + + foreach ($this->getOwnTraits() as $trait) { + if ($trait->hasProperty($name)) { + return true; + } + } + + return false; + } + + /** + * Returns reflections of properties imported from traits. + * + * @param integer $filter Properties filter + * @return array + */ + public function getTraitProperties($filter = null) + { + $properties = array(); + + foreach ($this->getOwnTraits() as $trait) { + foreach ($trait->getProperties(null) as $traitProperty) { + if (!isset($this->properties[$traitProperty->getName()]) && !isset($properties[$traitProperty->getName()])) { + $properties[$traitProperty->getName()] = $traitProperty->alias($this); + } + } + } + + if (null !== $filter) { + $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) { + return (bool) ($property->getModifiers() & $filter); + }); + } + + return array_values($properties); + } + + /** + * Returns default properties. + * + * @return array + */ + public function getDefaultProperties() + { + static $accessLevels = array(InternalReflectionProperty::IS_PUBLIC, InternalReflectionProperty::IS_PROTECTED, InternalReflectionProperty::IS_PRIVATE); + + $defaults = array(); + $properties = $this->getProperties(); + foreach (array(true, false) as $static) { + foreach ($properties as $property) { + foreach ($accessLevels as $level) { + if ($property->isStatic() === $static && ($property->getModifiers() & $level)) { + $defaults[$property->getName()] = $property->getDefaultValue(); + } + } + } + } + + return $defaults; + } + + /** + * Returns static properties reflections. + * + * @return array + */ + public function getStaticProperties() + { + $defaults = array(); + foreach ($this->getProperties(InternalReflectionProperty::IS_STATIC) as $property) { + if ($property instanceof ReflectionProperty) { + $defaults[$property->getName()] = $property->getDefaultValue(); + } + } + + return $defaults; + } + + /** + * Returns a value of a static property. + * + * @param string $name Property name + * @param mixed $default Default value + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible. + */ + public function getStaticPropertyValue($name, $default = null) + { + if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) { + if (!$property->isPublic() && !$property->isAccessible()) { + throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this); + } + + return $property->getDefaultValue(); + } + + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns traits used by this class. + * + * @return array + */ + public function getTraits() + { + $traitNames = $this->getTraitNames(); + if (empty($traitNames)) { + return array(); + } + + $broker = $this->getBroker(); + return array_combine($traitNames, array_map(function($traitName) use ($broker) { + return $broker->getClass($traitName); + }, $traitNames)); + } + + /** + * Returns traits used by this class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + $ownTraitNames = $this->getOwnTraitNames(); + if (empty($ownTraitNames)) { + return array(); + } + + $broker = $this->getBroker(); + return array_combine($ownTraitNames, array_map(function($traitName) use ($broker) { + return $broker->getClass($traitName); + }, $ownTraitNames)); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + $parentClass = $this->getParentClass(); + + $names = $parentClass ? $parentClass->getTraitNames() : array(); + foreach ($this->traits as $traitName) { + $names[] = $traitName; + } + + return array_unique($names); + } + + /** + * Returns names of traits used by this class an not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return $this->traits; + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return $this->traitAliases; + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return self::IS_TRAIT === $this->type; + } + + /** + * Returns if the class definition is valid. + * + * @return boolean + */ + public function isValid() + { + if (null !== $this->parentClassName && !$this->getParentClass()->isValid()) { + return false; + } + + foreach ($this->getInterfaces() as $interface) { + if (!$interface->isValid()) { + return false; + } + } + + foreach ($this->getTraits() as $trait) { + if (!$trait->isValid()) { + return false; + } + } + + return true; + } + + /** + * Returns if the class uses a particular trait. + * + * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. + */ + public function usesTrait($trait) + { + if (is_object($trait)) { + if (!$trait instanceof InternalReflectionClass && !$trait instanceof IReflectionClass) { + throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of trait reflection, "%s" provided.', get_class($trait)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $traitName = $trait->getName(); + + if (!$trait->isTrait()) { + throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + } else { + $reflection = $this->getBroker()->getClass($trait); + if (!$reflection->isTrait()) { + throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $traitName = $trait; + } + + return in_array($traitName, $this->getTraitNames()); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubclasses() + { + $that = $this->name; + return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { + if (!$class->isSubclassOf($that)) { + return false; + } + + return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that); + }); + } + + /** + * Returns names of direct subclasses. + * + * @return array + */ + public function getDirectSubclassNames() + { + return array_keys($this->getDirectSubclasses()); + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclasses() + { + $that = $this->name; + return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { + if (!$class->isSubclassOf($that)) { + return false; + } + + return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that); + }); + } + + /** + * Returns names of indirect subclasses. + * + * @return array + */ + public function getIndirectSubclassNames() + { + return array_keys($this->getIndirectSubclasses()); + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $that = $this->name; + return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { + if ($class->isInterface() || !$class->implementsInterface($that)) { + return false; + } + + return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that); + }); + } + + /** + * Returns names of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementerNames() + { + return array_keys($this->getDirectImplementers()); + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $that = $this->name; + return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { + if ($class->isInterface() || !$class->implementsInterface($that)) { + return false; + } + + return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that); + }); + } + + /** + * Returns names of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementerNames() + { + return array_keys($this->getIndirectImplementers()); + } + + /** + * Returns if the given object is an instance of this class. + * + * @param object $object Instance + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. + */ + public function isInstance($object) + { + if (!is_object($object)) { + throw new Exception\RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + return $this->name === get_class($object) || is_subclass_of($object, $this->getName()); + } + + /** + * Creates a new class instance without using a constructor. + * + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. + */ + public function newInstanceWithoutConstructor() + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new \TokenReflection\Php\ReflectionClass($this->getName(), $this->getBroker()); + return $reflection->newInstanceWithoutConstructor(); + } + + /** + * Creates a new instance using variable number of parameters. + * + * Use any number of constructor parameters as function parameters. + * + * @param mixed $args + * @return object + */ + public function newInstance($args) + { + return $this->newInstanceArgs(func_get_args()); + } + + /** + * Creates a new instance using an array of parameters. + * + * @param array $args Array of constructor parameters + * @return object + * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. + */ + public function newInstanceArgs(array $args = array()) + { + if (!class_exists($this->name, true)) { + throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $reflection = new InternalReflectionClass($this->name); + return $reflection->newInstanceArgs($args); + } + + /** + * Sets a static property value. + * + * @param string $name Property name + * @param mixed $value Property value + * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. + * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible. + */ + public function setStaticPropertyValue($name, $value) + { + if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) { + if (!$property->isPublic() && !$property->isAccessible()) { + throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this); + } + + $property->setDefaultValue($value); + return; + } + + throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + $implements = ''; + $interfaceNames = $this->getInterfaceNames(); + if (count($interfaceNames) > 0) { + $implements = sprintf( + ' %s %s', + $this->isInterface() ? 'extends' : 'implements', + implode(', ', $interfaceNames) + ); + } + + $buffer = ''; + $count = 0; + foreach ($this->getConstantReflections() as $constant) { + $buffer .= ' ' . $constant->__toString(); + $count++; + } + $constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer); + + $sBuffer = ''; + $sCount = 0; + $buffer = ''; + $count = 0; + foreach ($this->getProperties() as $property) { + $string = ' ' . preg_replace('~\n(?!$)~', "\n ", $property->__toString()); + if ($property->isStatic()) { + $sBuffer .= $string; + $sCount++; + } else { + $buffer .= $string; + $count++; + } + } + $staticProperties = sprintf("\n\n - Static properties [%d] {\n%s }", $sCount, $sBuffer); + $properties = sprintf("\n\n - Properties [%d] {\n%s }", $count, $buffer); + + $sBuffer = ''; + $sCount = 0; + $buffer = ''; + $count = 0; + foreach ($this->getMethods() as $method) { + // Skip private methods of parent classes + if ($method->getDeclaringClassName() !== $this->getName() && $method->isPrivate()) { + continue; + } + // Indent + $string = "\n "; + + $string .= preg_replace('~\n(?!$|\n|\s*\*)~', "\n ", $method->__toString()); + // Add inherits + if ($method->getDeclaringClassName() !== $this->getName()) { + $string = preg_replace( + array('~Method [ <[\w:]+~', '~, overwrites[^,]+~'), + array('\0, inherits ' . $method->getDeclaringClassName(), ''), + $string + ); + } + if ($method->isStatic()) { + $sBuffer .= $string; + $sCount++; + } else { + $buffer .= $string; + $count++; + } + } + $staticMethods = sprintf("\n\n - Static methods [%d] {\n%s }", $sCount, ltrim($sBuffer, "\n")); + $methods = sprintf("\n\n - Methods [%d] {\n%s }", $count, ltrim($buffer, "\n")); + + return sprintf( + "%s%s [ %s %s%s%s %s%s%s ] {\n @@ %s %d-%d%s%s%s%s%s\n}\n", + $this->getDocComment() ? $this->getDocComment() . "\n" : '', + $this->isInterface() ? 'Interface' : 'Class', + $this->isIterateable() ? ' ' : '', + $this->isAbstract() && !$this->isInterface() ? 'abstract ' : '', + $this->isFinal() ? 'final ' : '', + $this->isInterface() ? 'interface' : 'class', + $this->getName(), + null !== $this->getParentClassName() ? ' extends ' . $this->getParentClassName() : '', + $implements, + $this->getFileName(), + $this->getStartLine(), + $this->getEndLine(), + $constants, + $staticProperties, + $staticMethods, + $properties, + $methods + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object $className Class name or class instance + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $className, $return = false) + { + if (is_object($className)) { + $className = get_class($className); + } + + $class = $broker->getClass($className); + if ($class instanceof Invalid\ReflectionClass) { + throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); + } elseif ($class instanceof Dummy\ReflectionClass) { + throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST); + } + + if ($return) { + return $class->__toString(); + } + + echo $class->__toString(); + } + + /** + * Returns if the class definition is complete. + * + * @return boolean + */ + public function isComplete() + { + if (!$this->definitionComplete) { + if (null !== $this->parentClassName && !$this->getParentClass()->isComplete()) { + return false; + } + + foreach ($this->getOwnInterfaces() as $interface) { + if (!$interface->isComplete()) { + return false; + } + } + + $this->definitionComplete = true; + } + + return $this->definitionComplete; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->aliases; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\ParseException On invalid parent reflection provided + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionFileNamespace) { + throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT); + } + + $this->namespaceName = $parent->getName(); + $this->aliases = $parent->getNamespaceAliases(); + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionClass + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + return $this + ->parseModifiers($tokenStream) + ->parseName($tokenStream) + ->parseParent($tokenStream, $parent) + ->parseInterfaces($tokenStream, $parent); + } + + /** + * Parses class modifiers (abstract, final) and class type (class, interface). + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionClass + */ + private function parseModifiers(Stream $tokenStream) + { + while (true) { + switch ($tokenStream->getType()) { + case null: + break 2; + case T_ABSTRACT: + $this->modifiers = InternalReflectionClass::IS_EXPLICIT_ABSTRACT; + break; + case T_FINAL: + $this->modifiers = InternalReflectionClass::IS_FINAL; + break; + case T_INTERFACE: + $this->modifiers = self::IS_INTERFACE; + $this->type = self::IS_INTERFACE; + $tokenStream->skipWhitespaces(true); + break 2; + case T_TRAIT: + $this->modifiers = self::IS_TRAIT; + $this->type = self::IS_TRAIT; + $tokenStream->skipWhitespaces(true); + break 2; + case T_CLASS: + $tokenStream->skipWhitespaces(true); + break 2; + default: + break; + } + + $tokenStream->skipWhitespaces(true); + } + + return $this; + } + + /** + * Parses the class/interface name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\Exception\ParseException If the class name could not be determined. + */ + protected function parseName(Stream $tokenStream) + { + if (!$tokenStream->is(T_STRING)) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + if ($this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) { + $this->name = $tokenStream->getTokenValue(); + } else { + $this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue(); + } + + $tokenStream->skipWhitespaces(true); + + return $this; + } + + /** + * Parses the parent class. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionClass + */ + private function parseParent(Stream $tokenStream, ReflectionElement $parent = null) + { + if (!$tokenStream->is(T_EXTENDS)) { + return $this; + } + + while (true) { + $tokenStream->skipWhitespaces(true); + + $parentClassName = ''; + while (true) { + switch ($tokenStream->getType()) { + case T_STRING: + case T_NS_SEPARATOR: + $parentClassName .= $tokenStream->getTokenValue(); + break; + default: + break 2; + } + + $tokenStream->skipWhitespaces(true); + } + + $parentClassName = Resolver::resolveClassFQN($parentClassName, $this->aliases, $this->namespaceName); + + if ($this->isInterface()) { + $this->interfaces[] = $parentClassName; + + if (',' === $tokenStream->getTokenValue()) { + continue; + } + } else { + $this->parentClassName = $parentClassName; + } + + break; + } + + return $this; + } + + /** + * Parses implemented interfaces. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\Exception\ParseException On error while parsing interfaces. + */ + private function parseInterfaces(Stream $tokenStream, ReflectionElement $parent = null) + { + if (!$tokenStream->is(T_IMPLEMENTS)) { + return $this; + } + + if ($this->isInterface()) { + throw new Exception\ParseException($this, $tokenStream, 'Interfaces cannot implement interfaces.', Exception\ParseException::LOGICAL_ERROR); + } + + while (true) { + $interfaceName = ''; + + $tokenStream->skipWhitespaces(true); + while (true) { + switch ($tokenStream->getType()) { + case T_STRING: + case T_NS_SEPARATOR: + $interfaceName .= $tokenStream->getTokenValue(); + break; + default: + break 2; + } + + $tokenStream->skipWhitespaces(true); + } + + $this->interfaces[] = Resolver::resolveClassFQN($interfaceName, $this->aliases, $this->namespaceName); + + $type = $tokenStream->getType(); + if ('{' === $type) { + break; + } elseif (',' !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found, expected "{" or ";".', Exception\ParseException::UNEXPECTED_TOKEN); + } + } + + return $this; + } + + /** + * Parses child reflection objects from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\Exception\ParseException If a parse error was detected. + */ + protected function parseChildren(Stream $tokenStream, IReflection $parent) + { + while (true) { + switch ($type = $tokenStream->getType()) { + case null: + break 2; + case T_COMMENT: + case T_DOC_COMMENT: + $docblock = $tokenStream->getTokenValue(); + if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) { + array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock)); + } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) { + array_shift($this->docblockTemplates); + } + $tokenStream->next(); + break; + case '}': + break 2; + case T_PUBLIC: + case T_PRIVATE: + case T_PROTECTED: + case T_STATIC: + case T_VAR: + case T_VARIABLE: + static $searching = array(T_VARIABLE => true, T_FUNCTION => true); + + if (T_VAR !== $tokenStream->getType()) { + $position = $tokenStream->key(); + while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) { + $position++; + } + } + + if (T_VARIABLE === $type || T_VAR === $type) { + $property = new ReflectionProperty($tokenStream, $this->getBroker(), $this); + $this->properties[$property->getName()] = $property; + $tokenStream->next(); + break; + } + // Break missing on purpose + case T_FINAL: + case T_ABSTRACT: + case T_FUNCTION: + $method = new ReflectionMethod($tokenStream, $this->getBroker(), $this); + $this->methods[$method->getName()] = $method; + $tokenStream->next(); + break; + case T_CONST: + $tokenStream->skipWhitespaces(true); + while ($tokenStream->is(T_STRING)) { + $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this); + $this->constants[$constant->getName()] = $constant; + if ($tokenStream->is(',')) { + $tokenStream->skipWhitespaces(true); + } else { + $tokenStream->next(); + } + } + break; + case T_USE: + $tokenStream->skipWhitespaces(true); + + while (true) { + $traitName = ''; + $type = $tokenStream->getType(); + while (T_STRING === $type || T_NS_SEPARATOR === $type) { + $traitName .= $tokenStream->getTokenValue(); + $type = $tokenStream->skipWhitespaces(true)->getType(); + } + + if ('' === trim($traitName, '\\')) { + throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR); + } + + $this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName); + + if (';' === $type) { + // End of "use" + $tokenStream->skipWhitespaces(); + break; + } elseif (',' === $type) { + // Next trait name follows + $tokenStream->skipWhitespaces(); + continue; + } elseif ('{' !== $type) { + // Unexpected token + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN); + } + + // Aliases definition + $type = $tokenStream->skipWhitespaces(true)->getType(); + while (true) { + if ('}' === $type) { + $tokenStream->skipWhitespaces(); + break 2; + } + + $leftSide = ''; + $rightSide = array('', null); + $alias = true; + + while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) { + $leftSide .= $tokenStream->getTokenValue(); + $type = $tokenStream->skipWhitespaces(true)->getType(); + } + + if (T_INSTEADOF === $type) { + $alias = false; + } elseif (T_AS !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $type = $tokenStream->skipWhitespaces(true)->getType(); + + if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) { + if (!$alias) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + switch ($type) { + case T_PUBLIC: + $type = InternalReflectionMethod::IS_PUBLIC; + break; + case T_PROTECTED: + $type = InternalReflectionMethod::IS_PROTECTED; + break; + case T_PRIVATE: + $type = InternalReflectionMethod::IS_PRIVATE; + break; + default: + break; + } + + $rightSide[1] = $type; + $type = $tokenStream->skipWhitespaces(true)->getType(); + } + + while (T_STRING === $type || (T_NS_SEPARATOR === $type && !$alias)) { + $rightSide[0] .= $tokenStream->getTokenValue(); + $type = $tokenStream->skipWhitespaces(true)->getType(); + } + + if (empty($leftSide)) { + throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR); + } + + if ($alias) { + // Alias + if ($pos = strpos($leftSide, '::')) { + $methodName = substr($leftSide, $pos + 2); + $className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName); + $leftSide = $className . '::' . $methodName; + + $this->traitAliases[$rightSide[0]] = $leftSide; + } else { + $this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide; + } + + $this->traitImports[$leftSide][] = $rightSide; + } else { + // Insteadof + if ($pos = strpos($leftSide, '::')) { + $methodName = substr($leftSide, $pos + 2); + } else { + throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null; + } + + if (',' === $type) { + $tokenStream->skipWhitespaces(true); + continue; + } elseif (';' !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $type = $tokenStream->skipWhitespaces()->getType(); + } + } + + break; + default: + $tokenStream->next(); + break; + } + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionConstant.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionConstant.php new file mode 100644 index 00000000000..dbd06ad24e9 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionConstant.php @@ -0,0 +1,392 @@ +getName(); + if (null !== $this->namespaceName && $this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { + $name = substr($name, strlen($this->namespaceName) + 1); + } + + return $name; + } + + /** + * Returns the name of the declaring class. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClassName; + } + + /** + * Returns a reflection of the declaring class. + * + * @return \TokenReflection\ReflectionClass|null + */ + public function getDeclaringClass() + { + if (null === $this->declaringClassName) { + return null; + } + + return $this->getBroker()->getClass($this->declaringClassName); + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; + } + + /** + * Returns if the class is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return '' !== $this->getNamespaceName(); + } + + /** + * Returns the constant value. + * + * @return mixed + */ + public function getValue() + { + if (is_array($this->valueDefinition)) { + $this->value = Resolver::getValueDefinition($this->valueDefinition, $this); + $this->valueDefinition = Resolver::getSourceCode($this->valueDefinition); + } + + return $this->value; + } + + /** + * Returns the constant value definition. + * + * @return string + */ + public function getValueDefinition() + { + return is_array($this->valueDefinition) ? Resolver::getSourceCode($this->valueDefinition) : $this->valueDefinition; + } + + /** + * Returns the originaly provided value definition. + * + * @return string + */ + public function getOriginalValueDefinition() + { + return $this->valueDefinition; + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Constant [ %s %s ] { %s }\n", + strtolower(gettype($this->getValue())), + $this->getName(), + $this->getValue() + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object|null $class Class name, class instance or null + * @param string $constant Constant name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $class, $constant, $return = false) + { + $className = is_object($class) ? get_class($class) : $class; + $constantName = $constant; + + if (null === $className) { + $constant = $broker->getConstant($constantName); + if (null === $constant) { + throw new Exception\RuntimeException('Constant does not exist.', Exception\RuntimeException::DOES_NOT_EXIST); + } + } else { + $class = $broker->getClass($className); + if ($class instanceof Invalid\ReflectionClass) { + throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); + } elseif ($class instanceof Dummy\ReflectionClass) { + throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $class); + } + $constant = $class->getConstantReflection($constantName); + } + + if ($return) { + return $constant->__toString(); + } + + echo $constant->__toString(); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return null === $this->declaringClassName ? $this->aliases : $this->getDeclaringClass()->getNamespaceAliases(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return null === $this->declaringClassName ? parent::getPrettyName() : sprintf('%s::%s', $this->declaringClassName, $this->name); + } + + /** + * Returns if the constant definition is valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if ($parent instanceof ReflectionFileNamespace) { + $this->namespaceName = $parent->getName(); + $this->aliases = $parent->getNamespaceAliases(); + } elseif ($parent instanceof ReflectionClass) { + $this->declaringClassName = $parent->getName(); + } else { + throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT); + } + + return parent::processParent($parent, $tokenStream); + } + + /** + * Find the appropriate docblock. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection + * @return \TokenReflection\ReflectionConstant + */ + protected function parseDocComment(Stream $tokenStream, IReflection $parent) + { + $position = $tokenStream->key() - 1; + while ($position > 0 && !$tokenStream->is(T_CONST, $position)) { + $position--; + } + + $actual = $tokenStream->key(); + + parent::parseDocComment($tokenStream->seek($position), $parent); + + $tokenStream->seek($actual); + + return $this; + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionConstant + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + if ($tokenStream->is(T_CONST)) { + $tokenStream->skipWhitespaces(true); + } + + if (false === $this->docComment->getDocComment()) { + parent::parseDocComment($tokenStream, $parent); + } + + return $this + ->parseName($tokenStream) + ->parseValue($tokenStream, $parent); + } + + /** + * Parses the constant name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionConstant + * @throws \TokenReflection\Exception\ParseReflection If the constant name could not be determined. + */ + protected function parseName(Stream $tokenStream) + { + if (!$tokenStream->is(T_STRING)) { + throw new Exception\ParseException($this, $tokenStream, 'The constant name could not be determined.', Exception\ParseException::LOGICAL_ERROR); + } + + if (null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) { + $this->name = $tokenStream->getTokenValue(); + } else { + $this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue(); + } + + $tokenStream->skipWhitespaces(true); + + return $this; + } + + /** + * Parses the constant value. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionConstant + * @throws \TokenReflection\Exception\ParseException If the constant value could not be determined. + */ + private function parseValue(Stream $tokenStream, IReflection $parent) + { + if (!$tokenStream->is('=')) { + throw new Exception\ParseException($this, $tokenStream, 'Could not find the definition start.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $tokenStream->skipWhitespaces(true); + + static $acceptedTokens = array( + '-' => true, + '+' => true, + T_STRING => true, + T_NS_SEPARATOR => true, + T_CONSTANT_ENCAPSED_STRING => true, + T_DNUMBER => true, + T_LNUMBER => true, + T_DOUBLE_COLON => true, + T_CLASS_C => true, + T_DIR => true, + T_FILE => true, + T_FUNC_C => true, + T_LINE => true, + T_METHOD_C => true, + T_NS_C => true, + T_TRAIT_C => true + ); + + while (null !== ($type = $tokenStream->getType())) { + if (T_START_HEREDOC === $type) { + $this->valueDefinition[] = $tokenStream->current(); + while (null !== $type && T_END_HEREDOC !== $type) { + $tokenStream->next(); + $this->valueDefinition[] = $tokenStream->current(); + $type = $tokenStream->getType(); + }; + $tokenStream->next(); + } elseif (isset($acceptedTokens[$type])) { + $this->valueDefinition[] = $tokenStream->current(); + $tokenStream->next(); + } elseif ($tokenStream->isWhitespace(true)) { + $tokenStream->skipWhitespaces(true); + } else { + break; + } + } + + if (empty($this->valueDefinition)) { + throw new Exception\ParseException($this, $tokenStream, 'Value definition is empty.', Exception\ParseException::LOGICAL_ERROR); + } + + $value = $tokenStream->getTokenValue(); + if (null === $type || (',' !== $value && ';' !== $value)) { + throw new Exception\ParseException($this, $tokenStream, 'Invalid value definition.', Exception\ParseException::LOGICAL_ERROR); + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionElement.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionElement.php new file mode 100644 index 00000000000..b0fb331eb23 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionElement.php @@ -0,0 +1,352 @@ +count()) { + throw new Exception\ParseException($this, $tokenStream, 'Reflection token stream must not be empty.', Exception\ParseException::INVALID_ARGUMENT); + } + + parent::__construct($tokenStream, $broker, $parent); + } + + /** + * Parses the token substream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + */ + final protected function parseStream(Stream $tokenStream, IReflection $parent = null) + { + $this->fileName = $tokenStream->getFileName(); + + $this + ->processParent($parent, $tokenStream) + ->parseStartLine($tokenStream) + ->parseDocComment($tokenStream, $parent) + ->parse($tokenStream, $parent) + ->parseChildren($tokenStream, $parent) + ->parseEndLine($tokenStream); + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns a file reflection. + * + * @return \TokenReflection\ReflectionFile + * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker + */ + public function getFileReflection() + { + return $this->getBroker()->getFile($this->fileName); + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return $this->startLine; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->endLine; + } + + /** + * Returns the PHP extension reflection. + * + * Alwyas returns null - everything is user defined. + * + * @return null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * Alwyas returns false - everything is user defined. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns the appropriate source code part. + * + * @return string + */ + public function getSource() + { + return $this->broker->getFileTokens($this->getFileName())->getSourcePart($this->startPosition, $this->endPosition); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->startPosition; + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->endPosition; + } + + /** + * Returns the stack of docblock templates. + * + * @return array + */ + protected function getDocblockTemplates() + { + return $this->docblockTemplates; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\Reflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + // To be defined in child classes + return $this; + } + + /** + * Find the appropriate docblock. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection + * @return \TokenReflection\ReflectionElement + */ + protected function parseDocComment(Stream $tokenStream, IReflection $parent) + { + if ($this instanceof ReflectionParameter) { + $this->docComment = new ReflectionAnnotation($this); + return $this; + } + + $position = $tokenStream->key(); + + if ($tokenStream->is(T_DOC_COMMENT, $position - 1)) { + $value = $tokenStream->getTokenValue($position - 1); + if (self::DOCBLOCK_TEMPLATE_END !== $value) { + $this->docComment = new ReflectionAnnotation($this, $value); + $this->startPosition--; + } + } elseif ($tokenStream->is(T_DOC_COMMENT, $position - 2)) { + $value = $tokenStream->getTokenValue($position - 2); + if (self::DOCBLOCK_TEMPLATE_END !== $value) { + $this->docComment = new ReflectionAnnotation($this, $value); + $this->startPosition -= 2; + } + } elseif ($tokenStream->is(T_COMMENT, $position - 1) && preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $tokenStream->getTokenValue($position - 1))) { + $this->docComment = new ReflectionAnnotation($this, $tokenStream->getTokenValue($position - 1)); + $this->startPosition--; + } elseif ($tokenStream->is(T_COMMENT, $position - 2) && preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $tokenStream->getTokenValue($position - 2))) { + $this->docComment = new ReflectionAnnotation($this, $tokenStream->getTokenValue($position - 2)); + $this->startPosition -= 2; + } + + if (null === $this->docComment) { + $this->docComment = new ReflectionAnnotation($this); + } + + if ($parent instanceof ReflectionElement) { + $this->docComment->setTemplates($parent->getDocblockTemplates()); + } + + return $this; + } + + /** + * Saves the start line number. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token susbtream + * @return \TokenReflection\ReflectionElement + */ + private final function parseStartLine(Stream $tokenStream) + { + $token = $tokenStream->current(); + $this->startLine = $token[2]; + + $this->startPosition = $tokenStream->key(); + + return $this; + } + + /** + * Saves the end line number. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token susbtream + * @return \TokenReflection\ReflectionElement + */ + private final function parseEndLine(Stream $tokenStream) + { + $token = $tokenStream->current(); + $this->endLine = $token[2]; + + $this->endPosition = $tokenStream->key(); + + return $this; + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionElement + */ + abstract protected function parse(Stream $tokenStream, IReflection $parent); + + /** + * Parses the reflection object name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + */ + abstract protected function parseName(Stream $tokenStream); + + /** + * Parses child reflection objects from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\Reflection $parent Parent reflection object + * @return \TokenReflection\ReflectionElement + */ + protected function parseChildren(Stream $tokenStream, IReflection $parent) + { + // To be defined in child classes + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionFile.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionFile.php new file mode 100644 index 00000000000..863b3587299 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionFile.php @@ -0,0 +1,144 @@ +namespaces; + } + + /** + * Returns the string representation of the reflection object. + * + * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. + */ + public function __toString() + { + throw new Exception\RuntimeException('Casting to string is not supported.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string $argument Reflection object name + * @param boolean $return Return the export instead of outputting it + * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. + */ + public static function export(Broker $broker, $argument, $return = false) + { + throw new Exception\RuntimeException('Export is not supported.', Exception\RuntimeException::UNSUPPORTED); + } + + /** + * Outputs the file source code. + * + * @return string + */ + public function getSource() + { + return (string) $this->broker->getFileTokens($this->getName()); + } + + /** + * Parses the token substream and prepares namespace reflections from the file. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionFile + */ + protected function parseStream(Stream $tokenStream, IReflection $parent = null) + { + $this->name = $tokenStream->getFileName(); + + if (1 >= $tokenStream->count()) { + // No PHP content + $this->docComment = new ReflectionAnnotation($this, null); + return $this; + } + + $docCommentPosition = null; + + if (!$tokenStream->is(T_OPEN_TAG)) { + $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); + } else { + $tokenStream->skipWhitespaces(); + + while (null !== ($type = $tokenStream->getType())) { + switch ($type) { + case T_DOC_COMMENT: + if (null === $docCommentPosition) { + $docCommentPosition = $tokenStream->key(); + } + case T_WHITESPACE: + case T_COMMENT: + break; + case T_DECLARE: + // Intentionally twice call of skipWhitespaces() + $tokenStream + ->skipWhitespaces() + ->findMatchingBracket() + ->skipWhitespaces() + ->skipWhitespaces(); + break; + case T_NAMESPACE: + $docCommentPosition = $docCommentPosition ?: -1; + break 2; + default: + $docCommentPosition = $docCommentPosition ?: -1; + $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); + break 2; + } + + $tokenStream->skipWhitespaces(); + } + + while (null !== ($type = $tokenStream->getType())) { + if (T_NAMESPACE === $type) { + $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); + } else { + $tokenStream->skipWhitespaces(); + } + } + } + + if (null !== $docCommentPosition && !empty($this->namespaces) && $docCommentPosition === $this->namespaces[0]->getStartPosition()) { + $docCommentPosition = null; + } + $this->docComment = new ReflectionAnnotation($this, null !== $docCommentPosition ? $tokenStream->getTokenValue($docCommentPosition) : null); + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionFileNamespace.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionFileNamespace.php new file mode 100644 index 00000000000..6304256e457 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionFileNamespace.php @@ -0,0 +1,412 @@ +classes; + } + + /** + * Returns constant reflections. + * + * @return array + */ + public function getConstants() + { + return $this->constants; + } + + /** + * Returns function reflections. + * + * @return array + */ + public function getFunctions() + { + return $this->functions; + } + + /** + * Returns all imported namespaces and aliases. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->aliases; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionFile) { + throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFile.', Exception\ParseException::INVALID_PARENT); + } + + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionFileNamespace + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + return $this->parseName($tokenStream); + } + + /** + * Find the appropriate docblock. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection + * @return \TokenReflection\ReflectionElement + */ + protected function parseDocComment(Stream $tokenStream, IReflection $parent) + { + if (!$tokenStream->is(T_NAMESPACE)) { + $this->docComment = new ReflectionAnnotation($this); + return $this; + } else { + return parent::parseDocComment($tokenStream, $parent); + } + } + + /** + * Parses the namespace name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionFileNamespace + * @throws \TokenReflection\Exception\ParseException If the namespace name could not be determined. + */ + protected function parseName(Stream $tokenStream) + { + if (!$tokenStream->is(T_NAMESPACE)) { + $this->name = ReflectionNamespace::NO_NAMESPACE_NAME; + return $this; + } + + $tokenStream->skipWhitespaces(); + + $name = ''; + // Iterate over the token stream + while (true) { + switch ($tokenStream->getType()) { + // If the current token is a T_STRING, it is a part of the namespace name + case T_STRING: + case T_NS_SEPARATOR: + $name .= $tokenStream->getTokenValue(); + break; + default: + // Stop iterating when other token than string or ns separator found + break 2; + } + + $tokenStream->skipWhitespaces(true); + } + + $name = ltrim($name, '\\'); + + if (empty($name)) { + $this->name = ReflectionNamespace::NO_NAMESPACE_NAME; + } else { + $this->name = $name; + } + + if (!$tokenStream->is(';') && !$tokenStream->is('{')) { + throw new Exception\ParseException($this, $tokenStream, 'Invalid namespace name end, expecting ";" or "{".', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $tokenStream->skipWhitespaces(); + + return $this; + } + + /** + * Parses child reflection objects from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionFileNamespace + * @throws \TokenReflection\Exception\ParseException If child elements could not be parsed. + */ + protected function parseChildren(Stream $tokenStream, IReflection $parent) + { + static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); + $depth = 0; + + $firstChild = null; + + while (true) { + switch ($tokenStream->getType()) { + case T_USE: + while (true) { + $namespaceName = ''; + $alias = null; + + $tokenStream->skipWhitespaces(true); + + while (true) { + switch ($tokenStream->getType()) { + case T_STRING: + case T_NS_SEPARATOR: + $namespaceName .= $tokenStream->getTokenValue(); + break; + default: + break 2; + } + $tokenStream->skipWhitespaces(true); + } + $namespaceName = ltrim($namespaceName, '\\'); + + if (empty($namespaceName)) { + throw new Exception\ParseException($this, $tokenStream, 'Imported namespace name could not be determined.', Exception\ParseException::LOGICAL_ERROR); + } elseif ('\\' === substr($namespaceName, -1)) { + throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid namespace name "%s".', $namespaceName), Exception\ParseException::LOGICAL_ERROR); + } + + if ($tokenStream->is(T_AS)) { + // Alias defined + $tokenStream->skipWhitespaces(true); + + if (!$tokenStream->is(T_STRING)) { + throw new Exception\ParseException($this, $tokenStream, sprintf('The imported namespace "%s" seems aliased but the alias name could not be determined.', $namespaceName), Exception\ParseException::LOGICAL_ERROR); + } + + $alias = $tokenStream->getTokenValue(); + + $tokenStream->skipWhitespaces(true); + } else { + // No explicit alias + if (false !== ($pos = strrpos($namespaceName, '\\'))) { + $alias = substr($namespaceName, $pos + 1); + } else { + $alias = $namespaceName; + } + } + + if (isset($this->aliases[$alias])) { + throw new Exception\ParseException($this, $tokenStream, sprintf('Namespace alias "%s" already defined.', $alias), Exception\ParseException::LOGICAL_ERROR); + } + + $this->aliases[$alias] = $namespaceName; + + $type = $tokenStream->getType(); + if (';' === $type) { + $tokenStream->skipWhitespaces(); + break 2; + } elseif (',' === $type) { + // Next namespace in the current "use" definition + continue; + } + + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + case T_COMMENT: + case T_DOC_COMMENT: + $docblock = $tokenStream->getTokenValue(); + if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) { + array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock)); + } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) { + array_shift($this->docblockTemplates); + } + $tokenStream->next(); + break; + case '{': + $tokenStream->next(); + $depth++; + break; + case '}': + if (0 === $depth--) { + break 2; + } + + $tokenStream->next(); + break; + case null: + case T_NAMESPACE: + break 2; + case T_ABSTRACT: + case T_FINAL: + case T_CLASS: + case T_TRAIT: + case T_INTERFACE: + $class = new ReflectionClass($tokenStream, $this->getBroker(), $this); + $firstChild = $firstChild ?: $class; + + $className = $class->getName(); + if (isset($this->classes[$className])) { + if (!$this->classes[$className] instanceof Invalid\ReflectionClass) { + $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker()); + } + + if (!$this->classes[$className]->hasReasons()) { + $this->classes[$className]->addReason(new Exception\ParseException( + $this, + $tokenStream, + sprintf('Class %s is defined multiple times in the file.', $className), + Exception\ParseException::ALREADY_EXISTS + )); + } + } else { + $this->classes[$className] = $class; + } + $tokenStream->next(); + break; + case T_CONST: + $tokenStream->skipWhitespaces(true); + do { + $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this); + $firstChild = $firstChild ?: $constant; + + $constantName = $constant->getName(); + if (isset($this->constants[$constantName])) { + if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) { + $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker()); + } + + if (!$this->constants[$constantName]->hasReasons()) { + $this->constants[$constantName]->addReason(new Exception\ParseException( + $this, + $tokenStream, + sprintf('Constant %s is defined multiple times in the file.', $constantName), + Exception\ParseException::ALREADY_EXISTS + )); + } + } else { + $this->constants[$constantName] = $constant; + } + if ($tokenStream->is(',')) { + $tokenStream->skipWhitespaces(true); + } else { + $tokenStream->next(); + } + } while ($tokenStream->is(T_STRING)); + break; + case T_FUNCTION: + $position = $tokenStream->key() + 1; + while (isset($skipped[$type = $tokenStream->getType($position)])) { + $position++; + } + if ('(' === $type) { + // Skipping anonymous functions + + $tokenStream + ->seek($position) + ->findMatchingBracket() + ->skipWhiteSpaces(true); + + if ($tokenStream->is(T_USE)) { + $tokenStream + ->skipWhitespaces(true) + ->findMatchingBracket() + ->skipWhitespaces(true); + } + + $tokenStream + ->findMatchingBracket() + ->next(); + + continue; + } + + $function = new ReflectionFunction($tokenStream, $this->getBroker(), $this); + $firstChild = $firstChild ?: $function; + + $functionName = $function->getName(); + if (isset($this->functions[$functionName])) { + if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) { + $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker()); + } + + if (!$this->functions[$functionName]->hasReasons()) { + $this->functions[$functionName]->addReason(new Exception\ParseException( + $this, + $tokenStream, + sprintf('Function %s is defined multiple times in the file.', $functionName), + Exception\ParseException::ALREADY_EXISTS + )); + } + } else { + $this->functions[$functionName] = $function; + } + $tokenStream->next(); + break; + default: + $tokenStream->next(); + break; + } + } + + if ($firstChild) { + $this->startPosition = min($this->startPosition, $firstChild->getStartPosition()); + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionFunction.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionFunction.php new file mode 100644 index 00000000000..7ef59af1760 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionFunction.php @@ -0,0 +1,204 @@ +hasAnnotation('disabled'); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + $parameters = ''; + if ($this->getNumberOfParameters() > 0) { + $buffer = ''; + foreach ($this->getParameters() as $parameter) { + $buffer .= "\n " . $parameter->__toString(); + } + $parameters = sprintf( + "\n\n - Parameters [%d] {%s\n }", + $this->getNumberOfParameters(), + $buffer + ); + } + return sprintf( + "%sFunction [ function %s%s ] {\n @@ %s %d - %d%s\n}\n", + $this->getDocComment() ? $this->getDocComment() . "\n" : '', + $this->returnsReference() ? '&' : '', + $this->getName(), + $this->getFileName(), + $this->getStartLine(), + $this->getEndLine(), + $parameters + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string $function Function name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $function, $return = false) + { + $functionName = $function; + + $function = $broker->getFunction($functionName); + if (null === $function) { + throw new Exception\RuntimeException(sprintf('Function %s() does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST); + } + + if ($return) { + return $function->__toString(); + } + + echo $function->__toString(); + } + + /** + * Calls the function. + * + * @return mixed + */ + public function invoke() + { + return $this->invokeArgs(func_get_args()); + } + + /** + * Calls the function. + * + * @param array $args Function parameter values + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If the required function does not exist. + */ + public function invokeArgs(array $args = array()) + { + if (!function_exists($this->getName())) { + throw new Exception\RuntimeException('Could not invoke function; function is not defined.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return call_user_func_array($this->getName(), $args); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->aliases; + } + + /** + * Returns the function/method as closure. + * + * @return \Closure + */ + public function getClosure() + { + if (!function_exists($this->getName())) { + throw new Exception\RuntimeException('Could not invoke function; function is not defined.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $that = $this; + return function() use ($that) { + return $that->invokeArgs(func_get_args()); + }; + } + + /** + * Returns the closure scope class. + * + * @return null + */ + public function getClosureScopeClass() + { + return null; + } + + /** + * Returns if the function definition is valid. + * + * @return boolean + */ + public function isValid() + { + return true; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionFileNamespace) { + throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFileNamespace.', Exception\ParseException::INVALID_PARENT); + } + + $this->namespaceName = $parent->getName(); + $this->aliases = $parent->getNamespaceAliases(); + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionFunction + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + return $this + ->parseReturnsReference($tokenStream) + ->parseName($tokenStream); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionFunctionBase.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionFunctionBase.php new file mode 100644 index 00000000000..786ab1e9ca3 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionFunctionBase.php @@ -0,0 +1,440 @@ +namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName) { + return $this->namespaceName . '\\' . $this->name; + } + + return $this->name; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + return $this->name; + } + + /** + * Returns the namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; + } + + /** + * Returns if the function/method is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return '' !== $this->getNamespaceName(); + } + + /** + * Returns if the function/method is a closure. + * + * @return boolean + */ + public function isClosure() + { + return false; + } + + /** + * Returns this pointer bound to closure. + * + * @return null + */ + public function getClosureThis() + { + return null; + } + + /** + * Returns the closure scope class. + * + * @return string|null + */ + public function getClosureScopeClass() + { + return null; + } + + /** + * Returns if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return $this->returnsReference; + } + + /** + * Returns a particular function/method parameter. + * + * @param integer|string $parameter Parameter name or position + * @return \TokenReflection\ReflectionParameter + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. + * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. + */ + public function getParameter($parameter) + { + if (is_numeric($parameter)) { + if (!isset($this->parameters[$parameter])) { + throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + return $this->parameters[$parameter]; + } else { + foreach ($this->parameters as $reflection) { + if ($reflection->getName() === $parameter) { + return $reflection; + } + } + + throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } + + /** + * Returns parameters. + * + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Returns the number of parameters. + * + * @return integer + */ + public function getNumberOfParameters() + { + return count($this->parameters); + } + + /** + * Returns the number of required parameters. + * + * @return integer + */ + public function getNumberOfRequiredParameters() + { + $count = 0; + array_walk($this->parameters, function(ReflectionParameter $parameter) use (&$count) { + if (!$parameter->isOptional()) { + $count++; + } + }); + return $count; + } + + /** + * Returns static variables. + * + * @return array + */ + public function getStaticVariables() + { + if (empty($this->staticVariables) && !empty($this->staticVariablesDefinition)) { + foreach ($this->staticVariablesDefinition as $variableName => $variableDefinition) { + $this->staticVariables[$variableName] = Resolver::getValueDefinition($variableDefinition, $this); + } + } + + return $this->staticVariables; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name . '()'; + } + + /** + * Creates aliases to parameters. + * + * @throws \TokenReflection\Exception\RuntimeException When called on a ReflectionFunction instance. + */ + protected final function aliasParameters() + { + if (!$this instanceof ReflectionMethod) { + throw new Exception\RuntimeException('Only method parameters can be aliased.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + foreach ($this->parameters as $index => $parameter) { + $this->parameters[$index] = $parameter->alias($this); + } + } + + /** + * Parses if the function/method returns its value as reference. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionFunctionBase + * @throws \TokenReflection\Exception\ParseException If could not be determined if the function\method returns its value by reference. + */ + final protected function parseReturnsReference(Stream $tokenStream) + { + if (!$tokenStream->is(T_FUNCTION)) { + throw new Exception\ParseException($this, $tokenStream, 'Could not find the function keyword.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $tokenStream->skipWhitespaces(true); + + $type = $tokenStream->getType(); + + if ('&' === $type) { + $this->returnsReference = true; + $tokenStream->skipWhitespaces(true); + } elseif (T_STRING !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + return $this; + } + + /** + * Parses the function/method name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionMethod + * @throws \TokenReflection\Exception\ParseException If the class name could not be determined. + */ + final protected function parseName(Stream $tokenStream) + { + $this->name = $tokenStream->getTokenValue(); + + $tokenStream->skipWhitespaces(true); + + return $this; + } + + /** + * Parses child reflection objects from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionElement + */ + final protected function parseChildren(Stream $tokenStream, IReflection $parent) + { + return $this + ->parseParameters($tokenStream) + ->parseStaticVariables($tokenStream); + } + + /** + * Parses function/method parameters. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionFunctionBase + * @throws \TokenReflection\Exception\ParseException If parameters could not be parsed. + */ + final protected function parseParameters(Stream $tokenStream) + { + if (!$tokenStream->is('(')) { + throw new Exception\ParseException($this, $tokenStream, 'Could find the start token.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + static $accepted = array(T_NS_SEPARATOR => true, T_STRING => true, T_ARRAY => true, T_CALLABLE => true, T_VARIABLE => true, '&' => true); + + $tokenStream->skipWhitespaces(true); + + while (null !== ($type = $tokenStream->getType()) && ')' !== $type) { + if (isset($accepted[$type])) { + $parameter = new ReflectionParameter($tokenStream, $this->getBroker(), $this); + $this->parameters[] = $parameter; + } + + if ($tokenStream->is(')')) { + break; + } + + $tokenStream->skipWhitespaces(true); + } + + $tokenStream->skipWhitespaces(); + + return $this; + } + + /** + * Parses static variables. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionFunctionBase + * @throws \TokenReflection\Exception\ParseException If static variables could not be parsed. + */ + final protected function parseStaticVariables(Stream $tokenStream) + { + $type = $tokenStream->getType(); + if ('{' === $type) { + if ($this->getBroker()->isOptionSet(Broker::OPTION_PARSE_FUNCTION_BODY)) { + $tokenStream->skipWhitespaces(true); + + while ('}' !== ($type = $tokenStream->getType())) { + switch ($type) { + case T_STATIC: + $type = $tokenStream->skipWhitespaces(true)->getType(); + if (T_VARIABLE !== $type) { + // Late static binding + break; + } + + while (T_VARIABLE === $type) { + $variableName = $tokenStream->getTokenValue(); + $variableDefinition = array(); + + $type = $tokenStream->skipWhitespaces(true)->getType(); + if ('=' === $type) { + $type = $tokenStream->skipWhitespaces(true)->getType(); + $level = 0; + while ($tokenStream->valid()) { + switch ($type) { + case '(': + case '[': + case '{': + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + $level++; + break; + case ')': + case ']': + case '}': + $level--; + break; + case ';': + case ',': + if (0 === $level) { + break 2; + } + default: + break; + } + + $variableDefinition[] = $tokenStream->current(); + $type = $tokenStream->skipWhitespaces(true)->getType(); + } + + if (!$tokenStream->valid()) { + throw new Exception\ParseException($this, $tokenStream, 'Invalid end of token stream.', Exception\ParseException::READ_BEYOND_EOS); + } + } + + $this->staticVariablesDefinition[substr($variableName, 1)] = $variableDefinition; + + if (',' === $type) { + $type = $tokenStream->skipWhitespaces(true)->getType(); + } else { + break; + } + } + + break; + case T_FUNCTION: + // Anonymous function -> skip to its end + if (!$tokenStream->find('{')) { + throw new Exception\ParseException($this, $tokenStream, 'Could not find beginning of the anonymous function.', Exception\ParseException::UNEXPECTED_TOKEN); + } + // Break missing intentionally + case '{': + case '[': + case '(': + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + $tokenStream->findMatchingBracket()->skipWhitespaces(true); + break; + default: + $tokenStream->skipWhitespaces(); + break; + } + } + } else { + $tokenStream->findMatchingBracket(); + } + } elseif (';' !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionMethod.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionMethod.php new file mode 100644 index 00000000000..eb705426c69 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionMethod.php @@ -0,0 +1,775 @@ +declaringClassName ? null : $this->getBroker()->getClass($this->declaringClassName); + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClassName; + } + + /** + * Returns method modifiers. + * + * @return integer + */ + public function getModifiers() + { + if (!$this->modifiersComplete && !($this->modifiers & (self::ACCESS_LEVEL_CHANGED | self::IS_IMPLEMENTED_ABSTRACT))) { + $declaringClass = $this->getDeclaringClass(); + $parentClass = $declaringClass->getParentClass(); + if (false !== $parentClass && $parentClass->hasMethod($this->name)) { + $parentClassMethod = $parentClass->getMethod($this->name); + + // Access level changed + if (($this->isPublic() || $this->isProtected()) && $parentClassMethod->is(self::ACCESS_LEVEL_CHANGED | InternalReflectionMethod::IS_PRIVATE)) { + $this->modifiers |= self::ACCESS_LEVEL_CHANGED; + } + + // Implemented abstract + if ($parentClassMethod->isAbstract() && !$this->isAbstract()) { + $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT; + } + } else { + // Check if it is an implementation of an interface method + foreach ($declaringClass->getInterfaces() as $interface) { + if ($interface->hasOwnMethod($this->name)) { + $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT; + break; + } + } + } + + // Set if modifiers definition is complete + $this->modifiersComplete = $this->isComplete() || (($this->modifiers & self::IS_IMPLEMENTED_ABSTRACT) && ($this->modifiers & self::ACCESS_LEVEL_CHANGED)); + } + + return $this->modifiers; + } + + /** + * Returns if the method is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_ABSTRACT); + } + + /** + * Returns if the method is final. + * + * @return boolean + */ + public function isFinal() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_FINAL); + } + + /** + * Returns if the method is private. + * + * @return boolean + */ + public function isPrivate() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_PRIVATE); + } + + /** + * Returns if the method is protected. + * + * @return boolean + */ + public function isProtected() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_PROTECTED); + } + + /** + * Returns if the method is public. + * + * @return boolean + */ + public function isPublic() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_PUBLIC); + } + + /** + * Returns if the method is static. + * + * @return boolean + */ + public function isStatic() + { + return (bool) ($this->modifiers & InternalReflectionMethod::IS_STATIC); + } + + /** + * Shortcut for isPublic(), ... methods that allows or-ed modifiers. + * + * The {@see getModifiers()} method is called only when really necessary making this + * a more efficient way of doing + * + * if ($method->getModifiers() & $filter) { + * ... + * } + * + * + * @param integer $filter Filter + * @return boolean + */ + public function is($filter = null) + { + // See self::ACCESS_LEVEL_CHANGED | self::IS_IMPLEMENTED_ABSTRACT + static $computedModifiers = 0x808; + + if (null === $filter || ($this->modifiers & $filter)) { + return true; + } elseif (($filter & $computedModifiers) && !$this->modifiersComplete) { + return (bool) ($this->getModifiers() & $filter); + } + + return false; + } + + /** + * Returns if the method is a constructor. + * + * @return boolean + */ + public function isConstructor() + { + return (bool) ($this->modifiers & self::IS_CONSTRUCTOR); + } + + /** + * Returns if the method is a destructor. + * + * @return boolean + */ + public function isDestructor() + { + return (bool) ($this->modifiers & self::IS_DESTRUCTOR); + } + + /** + * Returns the method prototype. + * + * @return \TokenReflection\ReflectionMethod + * @throws \TokenReflection\Exception\RuntimeException If the method has no prototype. + */ + public function getPrototype() + { + if (null === $this->prototype) { + $prototype = null; + + $declaring = $this->getDeclaringClass(); + if (($parent = $declaring->getParentClass()) && $parent->hasMethod($this->name)) { + $method = $parent->getMethod($this->name); + + if (!$method->isPrivate()) { + try { + $prototype = $method->getPrototype(); + } catch (Exception\RuntimeException $e) { + $prototype = $method; + } + } + } + + if (null === $prototype) { + foreach ($declaring->getOwnInterfaces() as $interface) { + if ($interface->hasMethod($this->name)) { + $prototype = $interface->getMethod($this->name); + break; + } + } + } + + $this->prototype = $prototype ?: ($this->isComplete() ? false : null); + } + + if (empty($this->prototype)) { + throw new Exception\RuntimeException('Method has no prototype.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $this->prototype; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::%s', $this->declaringClassName ?: $this->declaringTraitName, parent::getPrettyName()); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + $internal = ''; + $overwrite = ''; + $prototype = ''; + + $declaringClassParent = $this->getDeclaringClass()->getParentClass(); + try { + $prototype = ', prototype ' . $this->getPrototype()->getDeclaringClassName(); + } catch (Exception\RuntimeException $e) { + if ($declaringClassParent && $declaringClassParent->isInternal()) { + $internal = 'internal:' . $parentClass->getExtensionName(); + } + } + + if ($declaringClassParent && $declaringClassParent->hasMethod($this->name)) { + $parentMethod = $declaringClassParent->getMethod($this->name); + $overwrite = ', overwrites ' . $parentMethod->getDeclaringClassName(); + } + + if ($this->isConstructor()) { + $cdtor = ', ctor'; + } elseif ($this->isDestructor()) { + $cdtor = ', dtor'; + } else { + $cdtor = ''; + } + + $parameters = ''; + if ($this->getNumberOfParameters() > 0) { + $buffer = ''; + foreach ($this->getParameters() as $parameter) { + $buffer .= "\n " . $parameter->__toString(); + } + $parameters = sprintf( + "\n\n - Parameters [%d] {%s\n }", + $this->getNumberOfParameters(), + $buffer + ); + } + // @todo support inherits + return sprintf( + "%sMethod [ <%s%s%s%s> %s%s%s%s%s%s method %s%s ] {\n @@ %s %d - %d%s\n}\n", + $this->getDocComment() ? $this->getDocComment() . "\n" : '', + !empty($internal) ? $internal : 'user', + $overwrite, + $prototype, + $cdtor, + $this->isAbstract() ? 'abstract ' : '', + $this->isFinal() ? 'final ' : '', + $this->isStatic() ? 'static ' : '', + $this->isPublic() ? 'public' : '', + $this->isPrivate() ? 'private' : '', + $this->isProtected() ? 'protected' : '', + $this->returnsReference() ? '&' : '', + $this->getName(), + $this->getFileName(), + $this->getStartLine(), + $this->getEndLine(), + $parameters + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object $class Class name or class instance + * @param string $method Method name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $class, $method, $return = false) + { + $className = is_object($class) ? get_class($class) : $class; + $methodName = $method; + + $class = $broker->getClass($className); + if ($class instanceof Invalid\ReflectionClass) { + throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); + } elseif ($class instanceof Dummy\ReflectionClass) { + throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); + } + $method = $class->getMethod($methodName); + + if ($return) { + return $method->__toString(); + } + + echo $method->__toString(); + } + + /** + * Calls the method on an given instance. + * + * @param object $object Class instance + * @param mixed $args + * @return mixed + */ + public function invoke($object, $args) + { + $params = func_get_args(); + return $this->invokeArgs(array_shift($params), $params); + } + + /** + * Calls the method on an given object. + * + * @param object $object Class instance + * @param array $args Method parameter values + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If it is not possible to invoke the method. + */ + public function invokeArgs($object, array $args = array()) + { + $declaringClass = $this->getDeclaringClass(); + if (!$declaringClass->isInstance($object)) { + throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + if ($this->isPublic()) { + return call_user_func_array(array($object, $this->getName()), $args); + } elseif ($this->isAccessible()) { + $refClass = new InternalReflectionClass($object); + $refMethod = $refClass->getMethod($this->name); + + $refMethod->setAccessible(true); + $value = $refMethod->invokeArgs($object, $args); + $refMethod->setAccessible(false); + + return $value; + } + + throw new Exception\RuntimeException('Only public methods can be invoked.', Exception\RuntimeException::NOT_ACCESSBILE, $this); + } + + /** + * Returns if the property is set accessible. + * + * @return boolean + */ + public function isAccessible() + { + return $this->accessible; + } + + /** + * Sets a method to be accessible or not. + * + * @param boolean $accessible + */ + public function setAccessible($accessible) + { + $this->accessible = (bool) $accessible; + } + + /** + * Returns if the definition is complete. + * + * Technically returns if the declaring class definition is complete. + * + * @return boolean + */ + private function isComplete() + { + return $this->getDeclaringClass()->isComplete(); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->getDeclaringClass()->getNamespaceAliases(); + } + + /** + * Returns the function/method as closure. + * + * @param object $object Object + * @return \Closure + */ + public function getClosure($object) + { + $declaringClass = $this->getDeclaringClass(); + if (!$declaringClass->isInstance($object)) { + throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $that = $this; + return function() use ($object, $that) { + return $that->invokeArgs($object, func_get_args()); + }; + } + + /** + * Creates a method alias of the given name and access level for the given class. + * + * @param \TokenReflection\ReflectionClass $parent New parent class + * @param string $name New method name + * @param integer $accessLevel New access level + * @return \TokenReflection\ReflectionMethod + * @throws \TokenReflection\Exception\RuntimeException If an invalid method access level was found. + */ + public function alias(ReflectionClass $parent, $name = null, $accessLevel = null) + { + static $possibleLevels = array(InternalReflectionMethod::IS_PUBLIC => true, InternalReflectionMethod::IS_PROTECTED => true, InternalReflectionMethod::IS_PRIVATE => true); + + $method = clone $this; + + $method->declaringClassName = $parent->getName(); + if (null !== $name) { + $method->originalName = $this->name; + $method->name = $name; + } + if (null !== $accessLevel) { + if (!isset($possibleLevels[$accessLevel])) { + throw new Exception\RuntimeException(sprintf('Invalid method access level: "%s".', $accessLevel), Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + $method->modifiers &= ~(InternalReflectionMethod::IS_PUBLIC | InternalReflectionMethod::IS_PROTECTED | InternalReflectionMethod::IS_PRIVATE); + $method->modifiers |= $accessLevel; + + $method->originalModifiers = $this->getModifiers(); + } + + foreach ($this->parameters as $parameterName => $parameter) { + $method->parameters[$parameterName] = $parameter->alias($method); + } + + return $method; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string|null + */ + public function getOriginalName() + { + return $this->originalName; + } + + /** + * Returns the original method when importing from a trait. + * + * @return \TokenReflection\IReflectionMethod|null + */ + public function getOriginal() + { + return $this->original; + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return integer|null + */ + public function getOriginalModifiers() + { + return $this->originalModifiers; + } + + /** + * Returns the defining trait. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getDeclaringTrait() + { + return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName); + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->declaringTraitName; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionClass) { + throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT); + } + + $this->declaringClassName = $parent->getName(); + if ($parent->isTrait()) { + $this->declaringTraitName = $parent->getName(); + } + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionMethod + * @throws \TokenReflection\Exception\Parse If the class could not be parsed. + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + return $this + ->parseBaseModifiers($tokenStream) + ->parseReturnsReference($tokenStream) + ->parseName($tokenStream) + ->parseInternalModifiers($parent); + } + + /** + * Parses base method modifiers (abstract, final, public, ...). + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionMethod + */ + private function parseBaseModifiers(Stream $tokenStream) + { + while (true) { + switch ($tokenStream->getType()) { + case T_ABSTRACT: + $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT; + break; + case T_FINAL: + $this->modifiers |= InternalReflectionMethod::IS_FINAL; + break; + case T_PUBLIC: + $this->modifiers |= InternalReflectionMethod::IS_PUBLIC; + break; + case T_PRIVATE: + $this->modifiers |= InternalReflectionMethod::IS_PRIVATE; + break; + case T_PROTECTED: + $this->modifiers |= InternalReflectionMethod::IS_PROTECTED; + break; + case T_STATIC: + $this->modifiers |= InternalReflectionMethod::IS_STATIC; + break; + case T_FUNCTION: + case null: + break 2; + default: + break; + } + + $tokenStream->skipWhitespaces(); + } + + if (!($this->modifiers & (InternalReflectionMethod::IS_PRIVATE | InternalReflectionMethod::IS_PROTECTED))) { + $this->modifiers |= InternalReflectionMethod::IS_PUBLIC; + } + + return $this; + } + + /** + * Parses internal PHP method modifiers (abstract, final, public, ...). + * + * @param \TokenReflection\ReflectionClass $class Parent class + * @return \TokenReflection\ReflectionMethod + */ + private function parseInternalModifiers(ReflectionClass $class) + { + $name = strtolower($this->name); + // In PHP 5.3.3+ the ctor can be named only __construct in namespaced classes + if ('__construct' === $name || ((!$class->inNamespace() || PHP_VERSION_ID < 50303) && strtolower($class->getShortName()) === $name)) { + $this->modifiers |= self::IS_CONSTRUCTOR; + } elseif ('__destruct' === $name) { + $this->modifiers |= self::IS_DESTRUCTOR; + } elseif ('__clone' === $name) { + $this->modifiers |= self::IS_CLONE; + } + + if ($class->isInterface()) { + $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT; + } else { + // Can be called statically, see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_API.c?revision=309853&view=markup#l1795 + static $notAllowed = array('__clone' => true, '__tostring' => true, '__get' => true, '__set' => true, '__isset' => true, '__unset' => true); + if (!$this->isStatic() && !$this->isConstructor() && !$this->isDestructor() && !isset($notAllowed[$name])) { + $this->modifiers |= self::IS_ALLOWED_STATIC; + } + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionNamespace.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionNamespace.php new file mode 100644 index 00000000000..527ca0e51e7 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionNamespace.php @@ -0,0 +1,558 @@ +name = $name; + $this->broker = $broker; + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns if the namespace is internal. + * + * Always false. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the namespace is user defined. + * + * Always true. + * + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns if the namespace contains a class of the given name. + * + * @param string $className Class name + * @return boolean + */ + public function hasClass($className) + { + $className = ltrim($className, '\\'); + if (false === strpos($className, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $className = $this->getName() . '\\' . $className; + } + + return isset($this->classes[$className]); + } + + /** + * Return a class reflection. + * + * @param string $className Class name + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\Exception\RuntimeException If the requested class reflection does not exist. + */ + public function getClass($className) + { + $className = ltrim($className, '\\'); + if (false === strpos($className, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $className = $this->getName() . '\\' . $className; + } + + if (!isset($this->classes[$className])) { + throw new Exception\RuntimeException(sprintf('Class "%s" does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $this->classes[$className]; + } + + /** + * Returns class reflections. + * + * @return array + */ + public function getClasses() + { + return $this->classes; + } + + /** + * Returns class names (FQN). + * + * @return array + */ + public function getClassNames() + { + return array_keys($this->classes); + } + + /** + * Returns class unqualified names (UQN). + * + * @return array + */ + public function getClassShortNames() + { + return array_map(function(IReflectionClass $class) { + return $class->getShortName(); + }, $this->classes); + } + + /** + * Returns if the namespace contains a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasConstant($constantName) + { + $constantName = ltrim($constantName, '\\'); + if (false === strpos($constantName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $constantName = $this->getName() . '\\' . $constantName; + } + + return isset($this->constants[$constantName]); + } + + /** + * Returns a constant reflection. + * + * @param string $constantName Constant name + * @return \TokenReflection\ReflectionConstant + * @throws \TokenReflection\Exception\RuntimeException If the required constant does not exist. + */ + public function getConstant($constantName) + { + $constantName = ltrim($constantName, '\\'); + if (false === strpos($constantName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $constantName = $this->getName() . '\\' . $constantName; + } + + if (!isset($this->constants[$constantName])) { + throw new Exception\RuntimeException(sprintf('Constant "%s" does not exist.', $constantName), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $this->constants[$constantName]; + } + + /** + * Returns constant reflections. + * + * @return array + */ + public function getConstants() + { + return $this->constants; + } + + /** + * Returns constant names (FQN). + * + * @return array + */ + public function getConstantNames() + { + return array_keys($this->constants); + } + + /** + * Returns constant unqualified names (UQN). + * + * @return array + */ + public function getConstantShortNames() + { + return array_map(function(IReflectionConstant $constant) { + return $constant->getShortName(); + }, $this->constants); + } + + /** + * Returns if the namespace contains a function of the given name. + * + * @param string $functionName Function name + * @return boolean + */ + public function hasFunction($functionName) + { + $functionName = ltrim($functionName, '\\'); + if (false === strpos($functionName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $functionName = $this->getName() . '\\' . $functionName; + } + + return isset($this->functions[$functionName]); + } + + /** + * Returns a function reflection. + * + * @param string $functionName Function name + * @return \TokenReflection\ReflectionFunction + * @throws \TokenReflection\Exception\RuntimeException If the required function does not exist. + */ + public function getFunction($functionName) + { + $functionName = ltrim($functionName, '\\'); + if (false === strpos($functionName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { + $functionName = $this->getName() . '\\' . $functionName; + } + + if (!isset($this->functions[$functionName])) { + throw new Exception\RuntimeException(sprintf('Function "%s" does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + return $this->functions[$functionName]; + } + + /** + * Returns function reflections. + * + * @return array + */ + public function getFunctions() + { + return $this->functions; + } + + /** + * Returns function names (FQN). + * + * @return array + */ + public function getFunctionNames() + { + return array_keys($this->functions); + } + + /** + * Returns function unqualified names (UQN). + * + * @return array + */ + public function getFunctionShortNames() + { + return array_map(function(IReflectionFunction $function) { + return $function->getShortName(); + }, $this->functions); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->name; + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + $buffer = ''; + $count = 0; + foreach ($this->getClasses() as $class) { + $string = "\n " . trim(str_replace("\n", "\n ", $class->__toString()), ' '); + $string = str_replace(" \n - Parameters", "\n - Parameters", $string); + + $buffer .= $string; + $count++; + } + $classes = sprintf("\n\n - Classes [%d] {\n%s }", $count, ltrim($buffer, "\n")); + + $buffer = ''; + $count = 0; + foreach ($this->getConstants() as $constant) { + $buffer .= ' ' . $constant->__toString(); + $count++; + } + $constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer); + + $buffer = ''; + $count = 0; + foreach ($this->getFunctions() as $function) { + $string = "\n " . trim(str_replace("\n", "\n ", $function->__toString()), ' '); + $string = str_replace(" \n - Parameters", "\n - Parameters", $string); + + $buffer .= $string; + $count++; + } + $functions = sprintf("\n\n - Functions [%d] {\n%s }", $count, ltrim($buffer, "\n")); + + return sprintf( + "Namespace [ namespace %s ] { %s%s%s\n}\n", + $this->getName(), + $classes, + $constants, + $functions + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string $namespace Namespace name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $namespace, $return = false) + { + $namespaceName = $namespace; + + $namespace = $broker->getNamespace($namespaceName); + if (null === $namespace) { + throw new Exception\RuntimeException(sprintf('Namespace %s does not exist.', $namespaceName), Exception\RuntimeException::DOES_NOT_EXIST); + } + + if ($return) { + return $namespace->__toString(); + } + + echo $namespace->__toString(); + } + + /** + * Adds a namespace part from a file. + * + * @param \TokenReflection\ReflectionFileNamespace $namespace Namespace part + * @return \TokenReflection\ReflectionNamespace + * @throws \TokenReflection\Exception\FileProcessingException If one of classes, functions or constants form the namespace are already defined + */ + public function addFileNamespace(ReflectionFileNamespace $namespace) + { + $errors = array(); + + foreach ($namespace->getClasses() as $className => $reflection) { + if ($reflection instanceof Invalid\ReflectionClass) { + $errors = array_merge($errors, $reflection->getReasons()); + } + + if (isset($this->classes[$className])) { + if (!$this->classes[$className] instanceof Invalid\ReflectionClass) { + $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker()); + } + + $error = new Exception\RuntimeException( + sprintf('Class %s was redeclared (previously declared in file %s).', $className, $this->classes[$className]->getFileName()), + Exception\RuntimeException::ALREADY_EXISTS, + $reflection + ); + $errors[] = $error; + $this->classes[$className]->addReason($error); + + if ($reflection instanceof Invalid\ReflectionClass) { + foreach ($reflection->getReasons() as $reason) { + $this->classes[$className]->addReason($reason); + } + } + } else { + $this->classes[$className] = $reflection; + } + } + + foreach ($namespace->getFunctions() as $functionName => $reflection) { + if ($reflection instanceof Invalid\ReflectionFunction) { + $errors = array_merge($errors, $reflection->getReasons()); + } + + if (isset($this->functions[$functionName])) { + if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) { + $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker()); + } + + $error = new Exception\RuntimeException( + sprintf('Function %s was redeclared (previousy declared in file %s).', $functionName, $this->functions[$functionName]->getFileName()), + Exception\RuntimeException::ALREADY_EXISTS, + $reflection + ); + $errors[] = $error; + $this->functions[$functionName]->addReason($error); + + if ($reflection instanceof Invalid\ReflectionFunction) { + foreach ($reflection->getReasons() as $reason) { + $this->functions[$functionName]->addReason($reason); + } + } + } else { + $this->functions[$functionName] = $reflection; + } + } + + foreach ($namespace->getConstants() as $constantName => $reflection) { + if ($reflection instanceof Invalid\ReflectionConstant) { + $errors = array_merge($errors, $reflection->getReasons()); + } + + if (isset($this->constants[$constantName])) { + if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) { + $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker()); + } + + $error = new Exception\RuntimeException( + sprintf('Constant %s was redeclared (previuosly declared in file %s).', $constantName, $this->constants[$constantName]->getFileName()), + Exception\RuntimeException::ALREADY_EXISTS, + $reflection + ); + $errors[] = $error; + $this->constants[$constantName]->addReason($error); + + if ($reflection instanceof Invalid\ReflectionConstant) { + foreach ($reflection->getReasons() as $reason) { + $this->constants[$constantName]->addReason($reason); + } + } + } else { + $this->constants[$constantName] = $reflection; + } + } + + if (!empty($errors)) { + throw new Exception\FileProcessingException($errors, null); + } + + return $this; + } + + /** + * Returns the appropriate source code part. + * + * Impossible for namespaces. + * + * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. + */ + public function getSource() + { + throw new Exception\RuntimeException('Cannot export source code of a namespace.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker|null + */ + public function getBroker() + { + return $this->broker; + } + + /** + * Magic __get method. + * + * @param string $key Variable name + * @return mixed + */ + final public function __get($key) + { + return ReflectionElement::get($this, $key); + } + + /** + * Magic __isset method. + * + * @param string $key Variable name + * @return boolean + */ + final public function __isset($key) + { + return ReflectionElement::exists($this, $key); + } +} \ No newline at end of file diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionParameter.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionParameter.php new file mode 100644 index 00000000000..d09945856c9 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionParameter.php @@ -0,0 +1,639 @@ +declaringClassName ? null : $this->getBroker()->getClass($this->declaringClassName); + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClassName; + } + + /** + * Returns the declaring function. + * + * @return \TokenReflection\ReflectionFunctionBase + */ + public function getDeclaringFunction() + { + if (null !== $this->declaringClassName) { + // Method parameter + $class = $this->getBroker()->getClass($this->declaringClassName); + if (null !== $class) { + return $class->getMethod($this->declaringFunctionName); + } + } else { + // Function parameter + return $this->getBroker()->getFunction($this->declaringFunctionName); + } + } + + /** + * Returns the declaring function name. + * + * @return string + */ + public function getDeclaringFunctionName() + { + return $this->declaringFunctionName; + } + + /** + * Returns the default value. + * + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If the property is not optional. + * @throws \TokenReflection\Exception\RuntimeException If the property has no default value. + */ + public function getDefaultValue() + { + if (!$this->isOptional()) { + throw new Exception\RuntimeException('Property is not optional.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + if (is_array($this->defaultValueDefinition)) { + if (0 === count($this->defaultValueDefinition)) { + throw new Exception\RuntimeException('Property has no default value.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this); + $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition); + } + + return $this->defaultValue; + } + + /** + * Returns the part of the source code defining the parameter default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition; + } + + /** + * Retutns if a default value for the parameter is available. + * + * @return boolean + */ + public function isDefaultValueAvailable() + { + return null !== $this->getDefaultValueDefinition(); + } + + /** + * Returns the position within all parameters. + * + * @return integer + */ + public function getPosition() + { + return $this->position; + } + + /** + * Returns if the parameter expects an array. + * + * @return boolean + */ + public function isArray() + { + return $this->typeHint === self::ARRAY_TYPE_HINT; + } + + /** + * Returns if the parameter expects a callback. + * + * @return boolean + */ + public function isCallable() + { + return $this->typeHint === self::CALLABLE_TYPE_HINT; + } + + /** + * Returns the original type hint as defined in the source code. + * + * @return string|null + */ + public function getOriginalTypeHint() + { + return !$this->isArray() && !$this->isCallable() ? ltrim($this->originalTypeHint, '\\') : null; + } + + /** + * Returns reflection of the required class of the value. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getClass() + { + $name = $this->getClassName(); + if (null === $name) { + return null; + } + + return $this->getBroker()->getClass($name); + } + + /** + * Returns the required class name of the value. + * + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If the type hint class FQN could not be determined. + */ + public function getClassName() + { + if ($this->isArray() || $this->isCallable()) { + return null; + } + + if (null === $this->typeHint && null !== $this->originalTypeHint) { + if (null !== $this->declaringClassName) { + $parent = $this->getDeclaringClass(); + if (null === $parent) { + throw new Exception\RuntimeException('Could not load class reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } else { + $parent = $this->getDeclaringFunction(); + if (null === $parent || !$parent->isTokenized()) { + throw new Exception\RuntimeException('Could not load function reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + } + + $lTypeHint = strtolower($this->originalTypeHint); + if ('parent' === $lTypeHint || 'self' === $lTypeHint) { + if (null === $this->declaringClassName) { + throw new Exception\RuntimeException('Parameter type hint cannot be "self" nor "parent" when not a method.', Exception\RuntimeException::UNSUPPORTED, $this); + } + + if ('parent' === $lTypeHint) { + if ($parent->isInterface() || null === $parent->getParentClassName()) { + throw new Exception\RuntimeException('Class has no parent.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $this->typeHint = $parent->getParentClassName(); + } else { + $this->typeHint = $this->declaringClassName; + } + } else { + $this->typeHint = ltrim(Resolver::resolveClassFQN($this->originalTypeHint, $parent->getNamespaceAliases(), $parent->getNamespaceName()), '\\'); + } + } + + return $this->typeHint; + } + + /** + * Returns if the the parameter allows NULL. + * + * @return boolean + */ + public function allowsNull() + { + if ($this->isArray() || $this->isCallable()) { + return 'null' === strtolower($this->getDefaultValueDefinition()); + } + + return null === $this->originalTypeHint || !empty($this->defaultValueDefinition); + } + + /** + * Returns if the parameter is optional. + * + * @return boolean + * @throws \TokenReflection\Exception\RuntimeException If it is not possible to determine if the parameter is optional. + */ + public function isOptional() + { + if (null === $this->isOptional) { + $function = $this->getDeclaringFunction(); + if (null === $function) { + throw new Exception\RuntimeException('Could not get the declaring function reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); + } + + $this->isOptional = true; + foreach (array_slice($function->getParameters(), $this->position) as $reflectionParameter) { + if (!$reflectionParameter->isDefaultValueAvailable()) { + $this->isOptional = false; + break; + } + } + } + + return $this->isOptional; + } + + /** + * Returns if the parameter value is passed by reference. + * + * @return boolean + */ + public function isPassedByReference() + { + return $this->passedByReference; + } + + /** + * Returns if the paramter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return !$this->isPassedByReference(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return str_replace('()', '($' . $this->name . ')', $this->getDeclaringFunction()->getPrettyName()); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + if ($this->getClass()) { + $hint = $this->getClassName(); + } elseif ($this->isArray()) { + $hint = self::ARRAY_TYPE_HINT; + } elseif ($this->isCallable()) { + $hint = self::CALLABLE_TYPE_HINT; + } else { + $hint = ''; + } + + if (!empty($hint) && $this->allowsNull()) { + $hint .= ' or NULL'; + } + + if ($this->isDefaultValueAvailable()) { + $default = ' = '; + if (null === $this->getDefaultValue()) { + $default .= 'NULL'; + } elseif (is_array($this->getDefaultValue())) { + $default .= 'Array'; + } elseif (is_bool($this->getDefaultValue())) { + $default .= $this->getDefaultValue() ? 'true' : 'false'; + } elseif (is_string($this->getDefaultValue())) { + $default .= sprintf("'%s'", str_replace("'", "\\'", $this->getDefaultValue())); + } else { + $default .= $this->getDefaultValue(); + } + } else { + $default = ''; + } + + return sprintf( + 'Parameter #%d [ <%s> %s%s$%s%s ]', + $this->getPosition(), + $this->isOptional() ? 'optional' : 'required', + $hint ? $hint . ' ' : '', + $this->isPassedByReference() ? '&' : '', + $this->getName(), + $default + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string $function Function name + * @param string $parameter Parameter name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $function, $parameter, $return = false) + { + $functionName = $function; + $parameterName = $parameter; + + $function = $broker->getFunction($functionName); + if (null === $function) { + throw new Exception\RuntimeException(sprintf('Function %s() does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST); + } + $parameter = $function->getParameter($parameterName); + + if ($return) { + return $parameter->__toString(); + } + + echo $parameter->__toString(); + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->getDeclaringFunction()->getNamespaceAliases(); + } + + /** + * Creates a parameter alias for the given method. + * + * @param \TokenReflection\ReflectionMethod $parent New parent method + * @return \TokenReflection\ReflectionParameter + */ + public function alias(ReflectionMethod $parent) + { + $parameter = clone $this; + + $parameter->declaringClassName = $parent->getDeclaringClassName(); + $parameter->declaringFunctionName = $parent->getName(); + + return $parameter; + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionFunctionBase) { + throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFunctionBase.', Exception\ParseException::INVALID_PARENT); + } + + // Declaring function name + $this->declaringFunctionName = $parent->getName(); + + // Position + $this->position = count($parent->getParameters()); + + // Declaring class name + if ($parent instanceof ReflectionMethod) { + $this->declaringClassName = $parent->getDeclaringClassName(); + } + + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionParameter + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + return $this + ->parseTypeHint($tokenStream) + ->parsePassedByReference($tokenStream) + ->parseName($tokenStream) + ->parseDefaultValue($tokenStream); + } + + /** + * Parses the type hint. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionParameter + * @throws \TokenReflection\Exception\ParseException If the type hint class name could not be determined. + */ + private function parseTypeHint(Stream $tokenStream) + { + $type = $tokenStream->getType(); + + if (T_ARRAY === $type) { + $this->typeHint = self::ARRAY_TYPE_HINT; + $this->originalTypeHint = self::ARRAY_TYPE_HINT; + $tokenStream->skipWhitespaces(true); + } elseif (T_CALLABLE === $type) { + $this->typeHint = self::CALLABLE_TYPE_HINT; + $this->originalTypeHint = self::CALLABLE_TYPE_HINT; + $tokenStream->skipWhitespaces(true); + } elseif (T_STRING === $type || T_NS_SEPARATOR === $type) { + $className = ''; + do { + $className .= $tokenStream->getTokenValue(); + + $tokenStream->skipWhitespaces(true); + $type = $tokenStream->getType(); + } while (T_STRING === $type || T_NS_SEPARATOR === $type); + + if ('' === ltrim($className, '\\')) { + throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid class name definition: "%s".', $className), Exception\ParseException::LOGICAL_ERROR); + } + + $this->originalTypeHint = $className; + } + + return $this; + } + + /** + * Parses if parameter value is passed by reference. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionParameter + */ + private function parsePassedByReference(Stream $tokenStream) + { + if ($tokenStream->is('&')) { + $this->passedByReference = true; + $tokenStream->skipWhitespaces(true); + } + + return $this; + } + + /** + * Parses the constant name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionParameter + * @throws \TokenReflection\Exception\ParseException If the parameter name could not be determined. + */ + protected function parseName(Stream $tokenStream) + { + if (!$tokenStream->is(T_VARIABLE)) { + throw new Exception\ParseException($this, $tokenStream, 'The parameter name could not be determined.', Exception\ParseException::UNEXPECTED_TOKEN); + } + + $this->name = substr($tokenStream->getTokenValue(), 1); + + $tokenStream->skipWhitespaces(true); + + return $this; + } + + /** + * Parses the parameter default value. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionParameter + * @throws \TokenReflection\Exception\ParseException If the default value could not be determined. + */ + private function parseDefaultValue(Stream $tokenStream) + { + if ($tokenStream->is('=')) { + $tokenStream->skipWhitespaces(true); + + $level = 0; + while (null !== ($type = $tokenStream->getType())) { + switch ($type) { + case ')': + if (0 === $level) { + break 2; + } + case '}': + case ']': + $level--; + break; + case '(': + case '{': + case '[': + $level++; + break; + case ',': + if (0 === $level) { + break 2; + } + break; + default: + break; + } + + $this->defaultValueDefinition[] = $tokenStream->current(); + $tokenStream->next(); + } + + if (')' !== $type && ',' !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'The property default value is not terminated properly. Expected "," or ")".', Exception\ParseException::UNEXPECTED_TOKEN); + } + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/ReflectionProperty.php b/apigen/libs/TokenReflection/TokenReflection/ReflectionProperty.php new file mode 100644 index 00000000000..f85a8cb11c4 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/ReflectionProperty.php @@ -0,0 +1,572 @@ +getBroker()->getClass($this->declaringClassName); + } + + /** + * Returns the name of the declaring class. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->declaringClassName; + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + if (is_array($this->defaultValueDefinition)) { + $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this); + $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition); + } + + return $this->defaultValue; + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition; + } + + /** + * Returns the property value for a particular class instance. + * + * @param object $object + * @return mixed + * @throws \TokenReflection\Exception\RuntimeException If it is not possible to return the property value. + */ + public function getValue($object) + { + $declaringClass = $this->getDeclaringClass(); + if (!$declaringClass->isInstance($object)) { + throw new Exception\RuntimeException('The given class is not an instance or subclass of the current class.', Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + if ($this->isPublic()) { + return $object->{$this->name}; + } elseif ($this->isAccessible()) { + $refClass = new InternalReflectionClass($object); + $refProperty = $refClass->getProperty($this->name); + + $refProperty->setAccessible(true); + $value = $refProperty->getValue($object); + $refProperty->setAccessible(false); + + return $value; + } + + throw new Exception\RuntimeException('Only public and accessible properties can return their values.', Exception\RuntimeException::NOT_ACCESSBILE, $this); + } + + /** + * Returns if the property was created at compile time. + * + * All properties in the source code are. + * + * @return boolean + */ + public function isDefault() + { + return true; + } + + /** + * Returns property modifiers. + * + * @return integer + */ + public function getModifiers() + { + if (false === $this->modifiersComplete) { + $declaringClass = $this->getDeclaringClass(); + $declaringClassParent = $declaringClass->getParentClass(); + + if ($declaringClassParent && $declaringClassParent->hasProperty($this->name)) { + $property = $declaringClassParent->getProperty($this->name); + if (($this->isPublic() && !$property->isPublic()) || ($this->isProtected() && $property->isPrivate())) { + $this->modifiers |= self::ACCESS_LEVEL_CHANGED; + } + } + + $this->modifiersComplete = ($this->modifiers & self::ACCESS_LEVEL_CHANGED) || $declaringClass->isComplete(); + } + + return $this->modifiers; + } + + /** + * Returns if the property is private. + * + * @return boolean + */ + public function isPrivate() + { + return (bool) ($this->modifiers & InternalReflectionProperty::IS_PRIVATE); + } + + /** + * Returns if the property is protected. + * + * @return boolean + */ + public function isProtected() + { + return (bool) ($this->modifiers & InternalReflectionProperty::IS_PROTECTED); + } + + /** + * Returns if the property is public. + * + * @return boolean + */ + public function isPublic() + { + return (bool) ($this->modifiers & InternalReflectionProperty::IS_PUBLIC); + } + + /** + * Returns if the poperty is static. + * + * @return boolean + */ + public function isStatic() + { + return (bool) ($this->modifiers & InternalReflectionProperty::IS_STATIC); + } + + /** + * Returns the string representation of the reflection object. + * + * @return string + */ + public function __toString() + { + return sprintf( + "Property [ %s%s%s%s%s\$%s ]\n", + $this->isStatic() ? '' : ' ', + $this->isPublic() ? 'public ' : '', + $this->isPrivate() ? 'private ' : '', + $this->isProtected() ? 'protected ' : '', + $this->isStatic() ? 'static ' : '', + $this->getName() + ); + } + + /** + * Exports a reflected object. + * + * @param \TokenReflection\Broker $broker Broker instance + * @param string|object $class Class name or class instance + * @param string $property Property name + * @param boolean $return Return the export instead of outputting it + * @return string|null + * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. + */ + public static function export(Broker $broker, $class, $property, $return = false) + { + $className = is_object($class) ? get_class($class) : $class; + $propertyName = $property; + + $class = $broker->getClass($className); + if ($class instanceof Invalid\ReflectionClass) { + throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); + } elseif ($class instanceof Dummy\ReflectionClass) { + throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); + } + $property = $class->getProperty($propertyName); + + if ($return) { + return $property->__toString(); + } + + echo $property->__toString(); + } + + /** + * Returns if the property is set accessible. + * + * @return boolean + */ + public function isAccessible() + { + return $this->accessible; + } + + /** + * Sets a property to be accessible or not. + * + * @param boolean $accessible If the property should be accessible. + */ + public function setAccessible($accessible) + { + $this->accessible = (bool) $accessible; + } + + /** + * Sets the property default value. + * + * @param mixed $value + */ + public function setDefaultValue($value) + { + $this->defaultValue = $value; + $this->defaultValueDefinition = var_export($value, true); + } + + /** + * Sets value of a property for a particular class instance. + * + * @param object $object Class instance + * @param mixed $value Poperty value + * @throws \TokenReflection\Exception\RuntimeException If it is not possible to set the property value. + */ + public function setValue($object, $value) + { + $declaringClass = $this->getDeclaringClass(); + if (!$declaringClass->isInstance($object)) { + throw new Exception\RuntimeException('Instance of or subclass expected.', Exception\RuntimeException::INVALID_ARGUMENT, $this); + } + + if ($this->isPublic()) { + $object->{$this->name} = $value; + } elseif ($this->isAccessible()) { + $refClass = new InternalReflectionClass($object); + $refProperty = $refClass->getProperty($this->name); + + $refProperty->setAccessible(true); + $refProperty->setValue($object, $value); + $refProperty->setAccessible(false); + + if ($this->isStatic()) { + $this->setDefaultValue($value); + } + } else { + throw new Exception\RuntimeException('Only public and accessible properties can be set.', Exception\RuntimeException::NOT_ACCESSBILE, $this); + } + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->getDeclaringClass()->getNamespaceAliases(); + } + + /** + * Creates a property alias for the given class. + * + * @param \TokenReflection\ReflectionClass $parent New parent class + * @return \TokenReflection\ReflectionProperty + */ + public function alias(ReflectionClass $parent) + { + $property = clone $this; + $property->declaringClassName = $parent->getName(); + return $property; + } + + /** + * Returns the defining trait. + * + * @return \TokenReflection\IReflectionClass|null + */ + public function getDeclaringTrait() + { + return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName); + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->declaringTraitName; + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::$%s', $this->declaringClassName ?: $this->declaringTraitName, $this->name); + } + + /** + * Processes the parent reflection object. + * + * @param \TokenReflection\IReflection $parent Parent reflection object + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionElement + * @throws \TokenReflection\Exception\Parse If an invalid parent reflection object was provided. + */ + protected function processParent(IReflection $parent, Stream $tokenStream) + { + if (!$parent instanceof ReflectionClass) { + throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT); + } + + $this->declaringClassName = $parent->getName(); + if ($parent->isTrait()) { + $this->declaringTraitName = $parent->getName(); + } + return parent::processParent($parent, $tokenStream); + } + + /** + * Parses reflected element metadata from the token stream. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\IReflection $parent Parent reflection object + * @return \TokenReflection\ReflectionProperty + */ + protected function parse(Stream $tokenStream, IReflection $parent) + { + $this->parseModifiers($tokenStream, $parent); + + if (false === $this->docComment->getDocComment()) { + $this->parseDocComment($tokenStream, $parent); + } + + return $this->parseName($tokenStream) + ->parseDefaultValue($tokenStream); + } + + /** + * Parses class modifiers (abstract, final) and class type (class, interface). + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @param \TokenReflection\ReflectionClass $class Defining class + * @return \TokenReflection\ReflectionClass + * @throws \TokenReflection\Exception\ParseException If the modifiers value cannot be determined. + */ + private function parseModifiers(Stream $tokenStream, ReflectionClass $class) + { + while (true) { + switch ($tokenStream->getType()) { + case T_PUBLIC: + case T_VAR: + $this->modifiers |= InternalReflectionProperty::IS_PUBLIC; + break; + case T_PROTECTED: + $this->modifiers |= InternalReflectionProperty::IS_PROTECTED; + break; + case T_PRIVATE: + $this->modifiers |= InternalReflectionProperty::IS_PRIVATE; + break; + case T_STATIC: + $this->modifiers |= InternalReflectionProperty::IS_STATIC; + break; + default: + break 2; + } + + $tokenStream->skipWhitespaces(true); + } + + if (InternalReflectionProperty::IS_STATIC === $this->modifiers) { + $this->modifiers |= InternalReflectionProperty::IS_PUBLIC; + } elseif (0 === $this->modifiers) { + $parentProperties = $class->getOwnProperties(); + if (empty($parentProperties)) { + throw new Exception\ParseException($this, $tokenStream, 'No access level defined and no previous defining class property present.', Exception\ParseException::LOGICAL_ERROR); + } + + $sibling = array_pop($parentProperties); + if ($sibling->isPublic()) { + $this->modifiers = InternalReflectionProperty::IS_PUBLIC; + } elseif ($sibling->isPrivate()) { + $this->modifiers = InternalReflectionProperty::IS_PRIVATE; + } elseif ($sibling->isProtected()) { + $this->modifiers = InternalReflectionProperty::IS_PROTECTED; + } else { + throw new Exception\ParseException($this, $tokenStream, sprintf('Property sibling "%s" has no access level defined.', $sibling->getName()), Exception\Parse::PARSE_ELEMENT_ERROR); + } + + if ($sibling->isStatic()) { + $this->modifiers |= InternalReflectionProperty::IS_STATIC; + } + } + + return $this; + } + + /** + * Parses the property name. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionProperty + * @throws \TokenReflection\Exception\ParseException If the property name could not be determined. + */ + protected function parseName(Stream $tokenStream) + { + if (!$tokenStream->is(T_VARIABLE)) { + throw new Exception\ParseException($this, $tokenStream, 'The property name could not be determined.', Exception\ParseException::LOGICAL_ERROR); + } + + $this->name = substr($tokenStream->getTokenValue(), 1); + + $tokenStream->skipWhitespaces(true); + + return $this; + } + + /** + * Parses the propety default value. + * + * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream + * @return \TokenReflection\ReflectionProperty + * @throws \TokenReflection\Exception\ParseException If the property default value could not be determined. + */ + private function parseDefaultValue(Stream $tokenStream) + { + $type = $tokenStream->getType(); + + if (';' === $type || ',' === $type) { + // No default value + return $this; + } + + if ('=' === $type) { + $tokenStream->skipWhitespaces(true); + } + + $level = 0; + while (null !== ($type = $tokenStream->getType())) { + switch ($type) { + case ',': + if (0 !== $level) { + break; + } + case ';': + break 2; + case ')': + case ']': + case '}': + $level--; + break; + case '(': + case '{': + case '[': + $level++; + break; + default: + break; + } + + $this->defaultValueDefinition[] = $tokenStream->current(); + $tokenStream->next(); + } + + if (',' !== $type && ';' !== $type) { + throw new Exception\ParseException($this, $tokenStream, 'The property default value is not terminated properly. Expected "," or ";".', Exception\ParseException::UNEXPECTED_TOKEN); + } + + return $this; + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Resolver.php b/apigen/libs/TokenReflection/TokenReflection/Resolver.php new file mode 100644 index 00000000000..4d97f183df5 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Resolver.php @@ -0,0 +1,288 @@ +getNamespaceName(); + } elseif ($reflection instanceof ReflectionParameter) { + $namespace = $reflection->getDeclaringFunction()->getNamespaceName(); + } elseif ($reflection instanceof ReflectionProperty || $reflection instanceof ReflectionMethod) { + $namespace = $reflection->getDeclaringClass()->getNamespaceName(); + } else { + throw new Exception\RuntimeException('Invalid reflection object given.', Exception\RuntimeException::INVALID_ARGUMENT, $reflection); + } + + // Process __LINE__ constants; replace with the line number of the corresponding token + foreach ($tokens as $index => $token) { + if (T_LINE === $token[0]) { + $tokens[$index] = array( + T_LNUMBER, + $token[2], + $token[2] + ); + } + } + + $source = self::getSourceCode($tokens); + + $constants = self::findConstants($tokens, $reflection); + if (!empty($constants)) { + foreach (array_reverse($constants, true) as $offset => $constant) { + $value = ''; + + try { + switch ($constant) { + case '__LINE__': + throw new Exception\RuntimeException('__LINE__ constant cannot be resolved this way.', Exception\RuntimeException::UNSUPPORTED, $reflection); + case '__FILE__': + $value = $reflection->getFileName(); + break; + case '__DIR__': + $value = dirname($reflection->getFileName()); + break; + case '__FUNCTION__': + if ($reflection instanceof IReflectionParameter) { + $value = $reflection->getDeclaringFunctionName(); + } elseif ($reflection instanceof IReflectionFunctionBase) { + $value = $reflection->getName(); + } + break; + case '__CLASS__': + if ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionParameter || $reflection instanceof IReflectionProperty || $reflection instanceof IReflectionMethod) { + $value = $reflection->getDeclaringClassName() ?: ''; + } + break; + case '__TRAIT__': + if ($reflection instanceof IReflectionMethod || $reflection instanceof IReflectionProperty) { + $value = $reflection->getDeclaringTraitName() ?: ''; + } elseif ($reflection instanceof IReflectionParameter) { + $method = $reflection->getDeclaringFunction(); + if ($method instanceof IReflectionMethod) { + $value = $method->getDeclaringTraitName() ?: ''; + } + } + break; + case '__METHOD__': + if ($reflection instanceof IReflectionParameter) { + if (null !== $reflection->getDeclaringClassName()) { + $value = $reflection->getDeclaringClassName() . '::' . $reflection->getDeclaringFunctionName(); + } else { + $value = $reflection->getDeclaringFunctionName(); + } + } elseif ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionProperty) { + $value = $reflection->getDeclaringClassName() ?: ''; + } elseif ($reflection instanceof IReflectionMethod) { + $value = $reflection->getDeclaringClassName() . '::' . $reflection->getName(); + } elseif ($reflection instanceof IReflectionFunction) { + $value = $reflection->getName(); + } + break; + case '__NAMESPACE__': + if (($reflection instanceof IReflectionConstant && null !== $reflection->getDeclaringClassName()) || $reflection instanceof IReflectionProperty) { + $value = $reflection->getDeclaringClass()->getNamespaceName(); + } elseif ($reflection instanceof IReflectionParameter) { + if (null !== $reflection->getDeclaringClassName()) { + $value = $reflection->getDeclaringClass()->getNamespaceName(); + } else { + $value = $reflection->getDeclaringFunction()->getNamespaceName(); + } + } elseif ($reflection instanceof IReflectionMethod) { + $value = $reflection->getDeclaringClass()->getNamespaceName(); + } else { + $value = $reflection->getNamespaceName(); + } + break; + default: + if (0 === stripos($constant, 'self::') || 0 === stripos($constant, 'parent::')) { + // Handle self:: and parent:: definitions + + if ($reflection instanceof ReflectionConstant) { + throw new Exception\RuntimeException('Constants cannot use self:: and parent:: references.', Exception\RuntimeException::UNSUPPORTED, $reflection); + } elseif ($reflection instanceof ReflectionParameter && null === $reflection->getDeclaringClassName()) { + throw new Exception\RuntimeException('Function parameters cannot use self:: and parent:: references.', Exception\RuntimeException::UNSUPPORTED, $reflection); + } + + if (0 === stripos($constant, 'self::')) { + $className = $reflection->getDeclaringClassName(); + } else { + $declaringClass = $reflection->getDeclaringClass(); + $className = $declaringClass->getParentClassName() ?: self::CONSTANT_NOT_FOUND; + } + + $constantName = $className . substr($constant, strpos($constant, '::')); + } else { + $constantName = self::resolveClassFQN($constant, $reflection->getNamespaceAliases(), $namespace); + if ($cnt = strspn($constant, '\\')) { + $constantName = str_repeat('\\', $cnt) . $constantName; + } + } + + $reflection = $reflection->getBroker()->getConstant($constantName); + $value = $reflection->getValue(); + } + } catch (Exception\RuntimeException $e) { + $value = self::CONSTANT_NOT_FOUND; + } + + $source = substr_replace($source, var_export($value, true), $offset, strlen($constant)); + } + } + + return self::evaluate(sprintf("return %s;\n", $source)); + } + + /** + * Returns a part of the source code defined by given tokens. + * + * @param array $tokens Tokens array + * @return array + */ + final public static function getSourceCode(array $tokens) + { + if (empty($tokens)) { + return null; + } + + $source = ''; + foreach ($tokens as $token) { + $source .= $token[1]; + } + return $source; + } + + /** + * Finds constant names in the token definition. + * + * @param array $tokens Tokenized source code + * @param \TokenReflection\ReflectionElement $reflection Caller reflection + * @return array + */ + final public static function findConstants(array $tokens, ReflectionElement $reflection) + { + static $accepted = array( + T_DOUBLE_COLON => true, + T_STRING => true, + T_NS_SEPARATOR => true, + T_CLASS_C => true, + T_DIR => true, + T_FILE => true, + T_LINE => true, + T_FUNC_C => true, + T_METHOD_C => true, + T_NS_C => true, + T_TRAIT_C => true + ); + static $dontResolve = array('true' => true, 'false' => true, 'null' => true); + + // Adding a dummy token to the end + $tokens[] = array(null); + + $constants = array(); + $constant = ''; + $offset = 0; + foreach ($tokens as $token) { + if (isset($accepted[$token[0]])) { + $constant .= $token[1]; + } elseif ('' !== $constant) { + if (!isset($dontResolve[strtolower($constant)])) { + $constants[$offset - strlen($constant)] = $constant; + } + $constant = ''; + } + + if (null !== $token[0]) { + $offset += strlen($token[1]); + } + } + return $constants; + } + + /** + * Evaluates a source code. + * + * @param string $source Source code + * @return mixed + */ + final private static function evaluate($source) { + return eval($source); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Stream/FileStream.php b/apigen/libs/TokenReflection/TokenReflection/Stream/FileStream.php new file mode 100644 index 00000000000..44e367562d2 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Stream/FileStream.php @@ -0,0 +1,50 @@ +fileName = Broker::getRealPath($fileName); + + if (false === $this->fileName) { + throw new Exception\StreamException($this, 'File does not exist.', Exception\StreamException::DOES_NOT_EXIST); + } + + $contents = @file_get_contents($this->fileName); + if (false === $contents) { + throw new Exception\StreamException($this, 'File is not readable.', Exception\StreamException::NOT_READABLE); + } + + $this->processSource($contents); + } +} \ No newline at end of file diff --git a/apigen/libs/TokenReflection/TokenReflection/Stream/StreamBase.php b/apigen/libs/TokenReflection/TokenReflection/Stream/StreamBase.php new file mode 100644 index 00000000000..85bdcee2ba1 --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Stream/StreamBase.php @@ -0,0 +1,487 @@ + true, T_WHITESPACE => true, T_DOC_COMMENT => true, T_INLINE_HTML => true, T_ENCAPSED_AND_WHITESPACE => true, T_CONSTANT_ENCAPSED_STRING => true); + + foreach ($stream as $position => $token) { + if (is_array($token)) { + if (!NATIVE_TRAITS && T_STRING === $token[0]) { + $lValue = strtolower($token[1]); + if ('trait' === $lValue) { + $token[0] = T_TRAIT; + } elseif ('insteadof' === $lValue) { + $token[0] = T_INSTEADOF; + } elseif ('__TRAIT__' === $token[1]) { + $token[0] = T_TRAIT_C; + } elseif ('callable' === $lValue) { + $token[0] = T_CALLABLE; + } + } + + $this->tokens[] = $token; + } else { + $previous = $this->tokens[$position - 1]; + $line = $previous[2]; + if (isset($checkLines[$previous[0]])) { + $line += substr_count($previous[1], "\n"); + } + + $this->tokens[] = array($token, $token, $line); + } + } + + $this->count = count($this->tokens); + } + + /** + * Returns the file name this is a part of. + * + * @return string + */ + public function getFileName() + { + return $this->fileName; + } + + /** + * Returns the original source code. + * + * @return string + */ + public function getSource() + { + return $this->getSourcePart(); + } + + /** + * Returns a part of the source code. + * + * @param mixed $start Start offset + * @param mixed $end End offset + * @return string + */ + public function getSourcePart($start = null, $end = null) + { + $start = (int) $start; + $end = null === $end ? ($this->count - 1) : (int) $end; + + $source = ''; + for ($i = $start; $i <= $end; $i++) { + $source .= $this->tokens[$i][1]; + } + return $source; + } + + /** + * Finds the position of the token of the given type. + * + * @param integer|string $type Token type + * @return \TokenReflection\Stream|boolean + */ + public function find($type) + { + $actual = $this->position; + while (isset($this->tokens[$this->position])) { + if ($type === $this->tokens[$this->position][0]) { + return $this; + } + + $this->position++; + } + + $this->position = $actual; + return false; + } + + /** + * Returns the position of the token with the matching bracket. + * + * @return \TokenReflection\Stream + * @throws \TokenReflection\Exception\RuntimeException If out of the token stream. + * @throws \TokenReflection\Exception\RuntimeException If there is no bracket at the current position. + * @throws \TokenReflection\Exception\RuntimeException If the matching bracket could not be found. + */ + public function findMatchingBracket() + { + static $brackets = array( + '(' => ')', + '{' => '}', + '[' => ']', + T_CURLY_OPEN => '}', + T_DOLLAR_OPEN_CURLY_BRACES => '}' + ); + + if (!$this->valid()) { + throw new Exception\StreamException($this, 'Out of token stream.', Exception\StreamException::READ_BEYOND_EOS); + } + + $position = $this->position; + + $bracket = $this->tokens[$this->position][0]; + + if (!isset($brackets[$bracket])) { + throw new Exception\StreamException($this, sprintf('There is no usable bracket at position "%d".', $position), Exception\StreamException::DOES_NOT_EXIST); + } + + $searching = $brackets[$bracket]; + + $level = 0; + while (isset($this->tokens[$this->position])) { + $type = $this->tokens[$this->position][0]; + if ($searching === $type) { + $level--; + } elseif ($bracket === $type || ($searching === '}' && ('{' === $type || T_CURLY_OPEN === $type || T_DOLLAR_OPEN_CURLY_BRACES === $type))) { + $level++; + } + + if (0 === $level) { + return $this; + } + + $this->position++; + } + + throw new Exception\StreamException($this, sprintf('Could not find the end bracket "%s" of the bracket at position "%d".', $searching, $position), Exception\StreamException::DOES_NOT_EXIST); + } + + /** + * Skips whitespaces and comments next to the current position. + * + * @param boolean $skipDocBlocks Skip docblocks as well + * @return \TokenReflection\Stream\StreamBase + */ + public function skipWhitespaces($skipDocBlocks = false) + { + static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); + + do { + $this->position++; + } while (isset($this->tokens[$this->position]) && isset($skipped[$this->tokens[$this->position][0]]) && ($skipDocBlocks || $this->tokens[$this->position][0] !== T_DOC_COMMENT)); + + return $this; + } + + /** + * Returns if the token stream is at a whitespace position. + * + * @param boolean $docBlock Consider docblocks as whitespaces + * @return boolean + */ + public function isWhitespace($docBlock = false) + { + static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => false); + + if (!$this->valid()) { + return false; + } + + return $docBlock ? isset($skipped[$this->getType()]) : !empty($skipped[$this->getType()]); + } + + /** + * Checks if there is a token of the given type at the given position. + * + * @param integer|string $type Token type + * @param integer $position Position; if none given, consider the current iteration position + * @return boolean + */ + public function is($type, $position = -1) + { + return $type === $this->getType($position); + } + + /** + * Returns the type of a token. + * + * @param integer $position Token position; if none given, consider the current iteration position + * @return string|integer|null + */ + public function getType($position = -1) + { + if (-1 === $position) { + $position = $this->position; + } + + return isset($this->tokens[$position]) ? $this->tokens[$position][0] : null; + } + + /** + * Returns the current token value. + * + * @param integer $position Token position; if none given, consider the current iteration position + * @return stirng + */ + public function getTokenValue($position = -1) + { + if (-1 === $position) { + $position = $this->position; + } + + return isset($this->tokens[$position]) ? $this->tokens[$position][1] : null; + } + + /** + * Returns the token type name. + * + * @param integer $position Token position; if none given, consider the current iteration position + * @return string|null + */ + public function getTokenName($position = -1) + { + $type = $this->getType($position); + if (is_string($type)) { + return $type; + } elseif (T_TRAIT === $type) { + return 'T_TRAIT'; + } elseif (T_INSTEADOF === $type) { + return 'T_INSTEADOF'; + } elseif (T_CALLABLE === $type) { + return 'T_CALLABLE'; + } + + return token_name($type); + } + + /** + * Stream serialization. + * + * @return string + */ + public function serialize() + { + return serialize(array($this->fileName, $this->tokens)); + } + + /** + * Restores the stream from the serialized state. + * + * @param string $serialized Serialized form + * @throws \TokenReflection\Exception\StreamException On deserialization error. + */ + public function unserialize($serialized) + { + $data = @unserialize($serialized); + if (false === $data) { + throw new Exception\StreamException($this, 'Could not deserialize the serialized data.', Exception\StreamException::SERIALIZATION_ERROR); + } + if (2 !== count($data) || !is_string($data[0]) || !is_array($data[1])) { + throw new Exception\StreamException($this, 'Invalid serialization data.', Exception\StreamException::SERIALIZATION_ERROR); + } + + $this->fileName = $data[0]; + $this->tokens = $data[1]; + $this->count = count($this->tokens); + $this->position = 0; + } + + /** + * Checks of there is a token with the given index. + * + * @param integer $offset Token index + * @return boolean + */ + public function offsetExists($offset) + { + return isset($this->tokens[$offset]); + } + + /** + * Removes a token. + * + * Unsupported. + * + * @param integer $offset Position + * @throws \TokenReflection\Exception\StreamException Unsupported. + */ + public function offsetUnset($offset) + { + throw new Exception\StreamException($this, 'Removing of tokens from the stream is not supported.', Exception\StreamException::UNSUPPORTED); + } + + /** + * Returns a token at the given index. + * + * @param integer $offset Token index + * @return mixed + */ + public function offsetGet($offset) + { + return isset($this->tokens[$offset]) ? $this->tokens[$offset] : null; + } + + /** + * Sets a value of a particular token. + * + * Unsupported + * + * @param integer $offset Position + * @param mixed $value Value + * @throws \TokenReflection\Exception\StreamException Unsupported. + */ + public function offsetSet($offset, $value) + { + throw new Exception\StreamException($this, 'Setting token values is not supported.', Exception\StreamException::UNSUPPORTED); + } + + /** + * Returns the current internal pointer value. + * + * @return integer + */ + public function key() + { + return $this->position; + } + + /** + * Advances the internal pointer. + * + * @return \TokenReflection\Stream + */ + public function next() + { + $this->position++; + return $this; + } + + /** + * Sets the internal pointer to zero. + * + * @return \TokenReflection\Stream + */ + public function rewind() + { + $this->position = 0; + return $this; + } + + /** + * Returns the current token. + * + * @return array|null + */ + public function current() + { + return isset($this->tokens[$this->position]) ? $this->tokens[$this->position] : null; + } + + /** + * Checks if there is a token on the current position. + * + * @return boolean + */ + public function valid() + { + return isset($this->tokens[$this->position]); + } + + /** + * Returns the number of tokens in the stream. + * + * @return integer + */ + public function count() + { + return $this->count; + } + + /** + * Sets the internal pointer to the given value. + * + * @param integer $position New position + * @return \TokenReflection\Stream + */ + public function seek($position) + { + $this->position = (int) $position; + return $this; + } + + /** + * Returns the stream source code. + * + * @return string + */ + public function __toString() + { + return $this->getSource(); + } +} diff --git a/apigen/libs/TokenReflection/TokenReflection/Stream/StringStream.php b/apigen/libs/TokenReflection/TokenReflection/Stream/StringStream.php new file mode 100644 index 00000000000..16f7c2b18fe --- /dev/null +++ b/apigen/libs/TokenReflection/TokenReflection/Stream/StringStream.php @@ -0,0 +1,38 @@ +fileName = $fileName; + $this->processSource($source); + } +} \ No newline at end of file diff --git a/apigen/templates/woodocs/404.latte b/apigen/templates/woodocs/404.latte new file mode 100644 index 00000000000..9b838179ee7 --- /dev/null +++ b/apigen/templates/woodocs/404.latte @@ -0,0 +1,23 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $robots = false} + +{block #title}Page not found{/block} + +{block #content} +
+

{include #title}

+

The requested page could not be found.

+

You have probably clicked on a link that is outdated and points to a page that does not exist any more or you have made an typing error in the address.

+

To continue please try to find requested page in the menu,{if $config->tree} take a look at the tree view of the whole project{/if} or use search field on the top.

+
+{/block} \ No newline at end of file diff --git a/apigen/templates/woodocs/@elementlist.latte b/apigen/templates/woodocs/@elementlist.latte new file mode 100644 index 00000000000..18a70f8bbd3 --- /dev/null +++ b/apigen/templates/woodocs/@elementlist.latte @@ -0,0 +1,59 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{define #elements} + + {if $namespace}{$element->shortName}{else}{$element->name}{/if} + {!$element|shortDescription} + +{/define} + +{if $classes} +

Classes summary

+ +{include #elements, elements => $classes} +
+{/if} + +{if $interfaces} +

Interfaces summary

+ +{include #elements, elements => $interfaces} +
+{/if} + +{if $traits} +

Traits summary

+ +{include #elements, elements => $traits} +
+{/if} + +{if $exceptions} +

Exceptions summary

+ +{include #elements, elements => $exceptions} +
+{/if} + +{if $constants} +

Constants summary

+ +{include #elements, elements => $constants} +
+{/if} + +{if $functions} +

Functions summary

+ +{include #elements, elements => $functions} +
+{/if} diff --git a/apigen/templates/woodocs/@layout.latte b/apigen/templates/woodocs/@layout.latte new file mode 100644 index 00000000000..f89cbefefe4 --- /dev/null +++ b/apigen/templates/woodocs/@layout.latte @@ -0,0 +1,186 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{default $robots = true} +{default $active = ''} + + + + + + + + {include #title}{if 'overview' !== $active && $config->title} | {$config->title}{/if} + + {var combinedJs = 'resources/combined.js'} + + {var elementListJs = 'elementlist.js'} + + {var bootstrapCss = 'resources/bootstrap.min.css'} + + {var styleCss = 'resources/style.css'} + + + + + + + + + +
+ +
+ +
+ + + + diff --git a/apigen/templates/woodocs/class.latte b/apigen/templates/woodocs/class.latte new file mode 100644 index 00000000000..ed90df10ccd --- /dev/null +++ b/apigen/templates/woodocs/class.latte @@ -0,0 +1,431 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'class'} + +{block #title}{if $class->deprecated}Deprecated {/if}{if $class->interface}Interface{elseif $class->trait}Trait{else}Class{/if} {$class->name}{/block} + +{block #content} +
+

{if $class->interface}Interface{elseif $class->trait}Trait{else}Class{/if} {$class->shortName}

+ + {if $class->valid} + +
+ {!$class|longDescription} +
+ +
+
+ Extended by + {if $item->documented} + {last}{/last}{$item->name}{last}{/last} + {else}{$item->name}{/if} + {var $itemOwnInterfaces = $item->ownInterfaces} + {if $itemOwnInterfaces} implements {foreach $itemOwnInterfaces as $interface} + {$interface->name}{sep}, {/sep} + {/foreach}{/if} + {var $itemOwnTraits = $item->ownTraits} + {if $itemOwnTraits} uses {foreach $itemOwnTraits as $trait} + {$trait->name}{sep}, {/sep} + {/foreach}{/if} +
+
+ + {define #children} +

+ {foreach $children as $child} + {$child->name}{sep}, {/sep} + {/foreach} +

+ {/define} + +
+

Direct known subclasses

+ {include #children, children => $directSubClasses} +
+ +
+

Indirect known subclasses

+ {include #children, children => $indirectSubClasses} +
+ +
+

Direct known implementers

+ {include #children, children => $directImplementers} +
+ +
+

Indirect known implementers

+ {include #children, children => $indirectImplementers} +
+ +
+

Direct Known Users

+ {include #children, children => $directUsers} +
+ +
+

Indirect Known Users

+ {include #children, children => $indirectUsers} +
+ +
+ {if !$class->interface && !$class->trait && ($class->abstract || $class->final)}{if $class->abstract}Abstract{else}Final{/if}
{/if} + {if $class->internal}PHP Extension: {$class->extension->name|firstUpper}
{/if} + {if $class->inNamespace()}Namespace: {!$class->namespaceName|namespaceLinks}
{/if} + {if $class->inPackage()}Package: {!$class->packageName|packageLinks}
{/if} + + {foreach $template->annotationSort($template->annotationFilter($class->annotations)) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {!$value|annotation:$annotation:$class}
+ {/foreach} + {/foreach} + {if $class->internal}Documented at php.net{else}Located at {$class->fileName|relativePath}{/if}
+
+ + {var $ownMethods = $class->ownMethods} + {var $inheritedMethods = $class->inheritedMethods} + {var $usedMethods = $class->usedMethods} + {var $ownMagicMethods = $class->ownMagicMethods} + {var $inheritedMagicMethods = $class->inheritedMagicMethods} + {var $usedMagicMethods = $class->usedMagicMethods} + + {if $ownMethods || $inheritedMethods || $usedMethods || $ownMagicMethods || $usedMagicMethods} + {define #method} + + {var $annotations = $method->annotations} + + + {if !$class->interface && $method->abstract}abstract{elseif $method->final}final{/if} {if $method->protected}protected{elseif $method->private}private{else}public{/if} {if $method->static}static{/if} + {ifset $annotations['return']}{!$annotations['return'][0]|typeLinks:$method}{/ifset} + {if $method->returnsReference()}&{/if} + + + +
+ # + {block|strip} + {if $class->internal} + {$method->name}( + {else} + {$method->name}( + {/if} + {foreach $method->parameters as $parameter} + {!$parameter->typeHint|typeLinks:$method} + {if $parameter->passedByReference}& {/if}${$parameter->name}{if $parameter->defaultValueAvailable} = {!$parameter->defaultValueDefinition|highlightPHP:$class}{elseif $parameter->unlimited},…{/if}{sep}, {/sep} + {/foreach} + ){/block} + + {if $config->template['options']['elementDetailsCollapsed']} +
+ {!$method|shortDescription:true} +
+ {/if} + +
+ {!$method|longDescription} + + {if !$class->deprecated && $method->deprecated} +

Deprecated

+ {ifset $annotations['deprecated']} +
+ {foreach $annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$method}
+ {/if} + {/foreach} +
+ {/ifset} + {/if} + + {if $method->parameters && isset($annotations['param'])} +

Parameters

+
+ {foreach $method->parameters as $parameter} +
${$parameter->name}{if $parameter->unlimited},…{/if}
+
{ifset $annotations['param'][$parameter->position]}{!$annotations['param'][$parameter->position]|annotation:'param':$method}{/ifset}
+ {/foreach} +
+ {/if} + + {if isset($annotations['return']) && 'void' !== $annotations['return'][0]} +

Returns

+
+ {foreach $annotations['return'] as $description} + {!$description|annotation:'return':$method}
+ {/foreach} +
+ {/if} + + {ifset $annotations['throws']} +

Throws

+
+ {foreach $annotations['throws'] as $description} + {!$description|annotation:'throws':$method}
+ {/foreach} +
+ {/ifset} + + {foreach $template->annotationSort($template->annotationFilter($annotations, array('deprecated', 'param', 'return', 'throws'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {!$description|annotation:$annotation:$method}
+ {/if} + {/foreach} +
+ {/foreach} + + {var $overriddenMethod = $method->overriddenMethod} + {if $overriddenMethod} +

Overrides

+ + {/if} + + {var $implementedMethod = $method->implementedMethod} + {if $implementedMethod} +

Implementation of

+ + {/if} +
+
+ + {/define} + +

Methods summary

+ + {foreach $ownMethods as $method} + {include #method, method => $method} + {/foreach} +
+ + {foreach $inheritedMethods as $parentName => $methods} +

Methods inherited from {$parentName}

+

+ {foreach $methods as $method} + {$method->name}(){sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMethods as $traitName => $methods} +

Methods used from {$traitName}

+

+ {foreach $methods as $data} + {$data['method']->name}(){if $data['aliases']}(as {foreach $data['aliases'] as $alias}{$alias->name}(){sep}, {/sep}{/foreach}){/if}{sep}, {/sep} + {/foreach} +

+ {/foreach} + +

Magic methods summary

+ + {foreach $ownMagicMethods as $method} + {include #method, method => $method} + {/foreach} +
+ + {foreach $inheritedMagicMethods as $parentName => $methods} +

Magic methods inherited from {$parentName}

+

+ {foreach $methods as $method} + {$method->name}(){sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMagicMethods as $traitName => $methods} +

Magic methods used from {$traitName}

+

+ {foreach $methods as $data} + {$data['method']->name}(){if $data['aliases']}(as {foreach $data['aliases'] as $alias}{$alias->name}(){sep}, {/sep}{/foreach}){/if}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + + {var $ownConstants = $class->ownConstants} + {var $inheritedConstants = $class->inheritedConstants} + + {if $ownConstants || $inheritedConstants} +

Constants summary

+ + + {var $annotations = $constant->annotations} + + + + + + +
{!$constant->typeHint|typeLinks:$constant} + {if $class->internal} + {$constant->name} + {else} + {$constant->name} + {/if} + {!$constant->valueDefinition|highlightValue:$class}
+ # + + {if $config->template['options']['elementDetailsCollapsed']} +
+ {!$constant|shortDescription:true} +
+ {/if} + +
+ {!$constant|longDescription} + + {foreach $template->annotationSort($template->annotationFilter($annotations, array('var'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {!$description|annotation:$annotation:$constant}
+ {/if} + {/foreach} +
+ {/foreach} +
+
+ + {foreach $inheritedConstants as $parentName => $constants} +

Constants inherited from {$parentName}

+

+ {foreach $constants as $constant} + {$constant->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + {var $ownProperties = $class->ownProperties} + {var $inheritedProperties = $class->inheritedProperties} + {var $usedProperties = $class->usedProperties} + {var $ownMagicProperties = $class->ownMagicProperties} + {var $inheritedMagicProperties = $class->inheritedMagicProperties} + {var $usedMagicProperties = $class->usedMagicProperties} + + {if $ownProperties || $inheritedProperties || $usedProperties || $ownMagicProperties || $inheritedMagicProperties || $usedMagicProperties} + {define #property} + + + {if $property->protected}protected{elseif $property->private}private{else}public{/if} {if $property->static}static{/if} {if $property->readOnly}read-only{elseif $property->writeOnly}write-only{/if} + {!$property->typeHint|typeLinks:$property} + + + + {if $class->internal} + ${$property->name} + {else} + ${$property->name} + {/if} + + {!$property->defaultValueDefinition|highlightValue:$class} +
+ # + + {if $config->template['options']['elementDetailsCollapsed']} +
+ {!$property|shortDescription:true} +
+ {/if} + +
+ {!$property|longDescription} + + {foreach $template->annotationSort($template->annotationFilter($property->annotations, array('var'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {!$description|annotation:$annotation:$property}
+ {/if} + {/foreach} +
+ {/foreach} +
+
+ + {/define} + +

Properties summary

+ + {foreach $ownProperties as $property} + {include #property, property => $property} + {/foreach} +
+ + {foreach $inheritedProperties as $parentName => $properties} +

Properties inherited from {$parentName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedProperties as $traitName => $properties} +

Properties used from {$traitName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {if $ownMagicProperties} +

Magic properties

+ + {foreach $ownMagicProperties as $property} + {include #property, property => $property} + {/foreach} +
+ {/if} + + {foreach $inheritedMagicProperties as $parentName => $properties} +

Magic properties inherited from {$parentName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMagicProperties as $traitName => $properties} +

Magic properties used from {$traitName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + {else} +
+

+ Documentation of this class could not be generated. +

+

+ Class was originally declared in {$class->fileName|relativePath} and is invalid because of: +

+
    +
  • Class was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/apigen/templates/woodocs/combined.js.latte b/apigen/templates/woodocs/combined.js.latte new file mode 100644 index 00000000000..ad9fa3fb21d --- /dev/null +++ b/apigen/templates/woodocs/combined.js.latte @@ -0,0 +1,22 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{contentType javascript} + +var ApiGen = ApiGen || {}; +ApiGen.config = {$config->template}; + +{var $scripts = ['jquery.min.js', 'jquery.cookie.js', 'jquery.sprintf.js', 'jquery.autocomplete.js', 'jquery.sortElements.js', 'main.js']} +{var $dir = dirname($template->getFile())} + +{foreach $scripts as $script} +{!file_get_contents("$dir/js/$script")} +{/foreach} diff --git a/apigen/templates/woodocs/config.neon b/apigen/templates/woodocs/config.neon new file mode 100644 index 00000000000..6ddc2f6951a --- /dev/null +++ b/apigen/templates/woodocs/config.neon @@ -0,0 +1,56 @@ +require: + min: 2.8.0 + +resources: + resources: resources + +templates: + common: + overview.latte: index.html + combined.js.latte: resources/combined.js + elementlist.js.latte: elementlist.js + 404.latte: 404.html + + main: + package: + filename: package-%s.html + template: package.latte + namespace: + filename: namespace-%s.html + template: namespace.latte + class: + filename: class-%s.html + template: class.latte + constant: + filename: constant-%s.html + template: constant.latte + function: + filename: function-%s.html + template: function.latte + source: + filename: source-%s.html + template: source.latte + tree: + filename: tree.html + template: tree.latte + deprecated: + filename: deprecated.html + template: deprecated.latte + todo: + filename: todo.html + template: todo.latte + + optional: + sitemap: + filename: sitemap.xml + template: sitemap.xml.latte + opensearch: + filename: opensearch.xml + template: opensearch.xml.latte + robots: + filename: robots.txt + template: robots.txt.latte + +options: + elementDetailsCollapsed: Yes + elementsOrder: natural # alphabetical diff --git a/apigen/templates/woodocs/constant.latte b/apigen/templates/woodocs/constant.latte new file mode 100644 index 00000000000..43ad423962f --- /dev/null +++ b/apigen/templates/woodocs/constant.latte @@ -0,0 +1,67 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'constant'} + +{block #title}{if $constant->deprecated}Deprecated {/if}Constant {$constant->name}{/block} + +{block #content} +
+

Constant {$constant->shortName}

+ + {if $constant->valid} + +
+ {!$constant|longDescription} +
+ +
+ {if $constant->inNamespace()}Namespace: {!$constant->namespaceName|namespaceLinks}
{/if} + {if $constant->inPackage()}Package: {!$constant->packageName|packageLinks}
{/if} + {foreach $template->annotationSort($template->annotationFilter($constant->annotations, array('var'))) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {!$value|annotation:$annotation:$constant}
+ {/foreach} + {/foreach} + Located at {$constant->fileName|relativePath}
+
+ + {var $annotations = $constant->annotations} + +

Value summary

+ + + + + + +
{!$constant->typeHint|typeLinks:$constant}{block|strip} + {var $element = $template->resolveElement($constant->valueDefinition, $constant)} + {if $element}{$constant->valueDefinition}{else}{!$constant->valueDefinition|highlightValue:$constant}{/if} + {/block}{ifset $annotations['var']}{!$annotations['var'][0]|description:$constant}{/ifset}
+ + {else} +
+

+ Documentation of this constant could not be generated. +

+

+ Constant was originally declared in {$constant->fileName|relativePath} and is invalid because of: +

+
    +
  • Constant was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/apigen/templates/woodocs/deprecated.latte b/apigen/templates/woodocs/deprecated.latte new file mode 100644 index 00000000000..427afc7c5b6 --- /dev/null +++ b/apigen/templates/woodocs/deprecated.latte @@ -0,0 +1,137 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'deprecated'} + +{block #title}Deprecated{/block} + +{block #content} +
+

{include #title}

+ + {define #classes} + + {$class->name} + + {foreach $class->annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$class}
+ {/if} + {/foreach} + + + {/define} + + {if $deprecatedClasses} +

Classes summary

+ + {include #classes, items => $deprecatedClasses} +
+ {/if} + + {if $deprecatedInterfaces} +

Interfaces summary

+ + {include #classes, items => $deprecatedInterfaces} +
+ {/if} + + {if $deprecatedTraits} +

Traits summary

+ + {include #classes, items => $deprecatedTraits} +
+ {/if} + + {if $deprecatedExceptions} +

Exceptions summary

+ + {include #classes, items => $deprecatedExceptions} +
+ {/if} + + {if $deprecatedMethods} +

Methods summary

+ + + + + + +
{$method->declaringClassName}{$method->name}() + {if $method->hasAnnotation('deprecated')} + {foreach $method->annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$method}
+ {/if} + {/foreach} + {/if} +
+ {/if} + + {if $deprecatedConstants} +

Constants summary

+ + + {if $constant->declaringClassName} + + + {else} + + + {/if} + + +
{$constant->declaringClassName}{$constant->name}{$constant->namespaceName}{$constant->shortName} + {foreach $constant->annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$constant}
+ {/if} + {/foreach} +
+ {/if} + + {if $deprecatedProperties} +

Properties summary

+ + + + + + +
{$property->declaringClassName}${$property->name} + {foreach $property->annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$property}
+ {/if} + {/foreach} +
+ {/if} + + {if $deprecatedFunctions} +

Functions summary

+ + + + + + +
{$function->namespaceName}{$function->shortName} + {foreach $function->annotations['deprecated'] as $description} + {if $description} + {!$description|annotation:'deprecated':$function}
+ {/if} + {/foreach} +
+ {/if} +
+{/block} diff --git a/apigen/templates/woodocs/elementlist.js.latte b/apigen/templates/woodocs/elementlist.js.latte new file mode 100644 index 00000000000..176b164dfd4 --- /dev/null +++ b/apigen/templates/woodocs/elementlist.js.latte @@ -0,0 +1,15 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{contentType javascript} + +var ApiGen = ApiGen || {}; +ApiGen.elements = {$elements}; diff --git a/apigen/templates/woodocs/function.latte b/apigen/templates/woodocs/function.latte new file mode 100644 index 00000000000..fba90ca4eb1 --- /dev/null +++ b/apigen/templates/woodocs/function.latte @@ -0,0 +1,98 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'function'} + +{block #title}{if $function->deprecated}Deprecated {/if}Function {$function->name}{/block} + +{block #content} +
+

Function {$function->shortName}

+ + {if $function->valid} + +
+ {!$function|longDescription} +
+ +
+ {if $function->inNamespace()}Namespace: {!$function->namespaceName|namespaceLinks}
{/if} + {if $function->inPackage()}Package: {!$function->packageName|packageLinks}
{/if} + {foreach $template->annotationSort($template->annotationFilter($function->annotations, array('param', 'return', 'throws'))) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {!$value|annotation:$annotation:$function}
+ {/foreach} + {/foreach} + Located at {$function->fileName|relativePath}
+
+ + {var $annotations = $function->annotations} + + {if $function->numberOfParameters} +

Parameters summary

+ + + + + + +
{!$parameter->typeHint|typeLinks:$function}{block|strip} + {if $parameter->passedByReference}& {/if}${$parameter->name}{if $parameter->defaultValueAvailable} = {!$parameter->defaultValueDefinition|highlightPHP:$function}{elseif $parameter->unlimited},…{/if} + {/block} + {ifset $annotations['param'][$parameter->position]}{!$annotations['param'][$parameter->position]|description:$parameter}{/ifset} +
+ {/if} + + {if isset($annotations['return']) && 'void' !== $annotations['return'][0]} +

Return value summary

+ + + + + +
+ {!$annotations['return'][0]|typeLinks:$function} + + {!$annotations['return'][0]|description:$function} +
+ {/if} + + {if isset($annotations['throws'])} +

Thrown exceptions summary

+ + + + + +
+ {!$throws|typeLinks:$function} + + {!$throws|description:$function} +
+ {/if} + + {else} +
+

+ Documentation of this function could not be generated. +

+

+ Function was originally declared in {$function->fileName|relativePath} and is invalid because of: +

+
    +
  • Function was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/apigen/templates/woodocs/js/jquery.autocomplete.js b/apigen/templates/woodocs/js/jquery.autocomplete.js new file mode 100644 index 00000000000..b8bec34df5a --- /dev/null +++ b/apigen/templates/woodocs/js/jquery.autocomplete.js @@ -0,0 +1,799 @@ +/*! + * jQuery Autocomplete plugin 1.1 + * + * Copyright (c) 2009 Jörn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ + */ + +;(function($) { + +$.fn.extend({ + autocomplete: function(urlOrData, options) { + var isUrl = typeof urlOrData == "string"; + options = $.extend({}, $.Autocompleter.defaults, { + url: isUrl ? urlOrData : null, + data: isUrl ? null : urlOrData, + delay: isUrl ? $.Autocompleter.defaults.delay : 10, + max: options && !options.scroll ? 10 : 150 + }, options); + + // if highlight is set to false, replace it with a do-nothing function + options.highlight = options.highlight || function(value) { return value; }; + + // if the formatMatch option is not specified, then use formatItem for backwards compatibility + options.formatMatch = options.formatMatch || options.formatItem; + + options.show = options.show || function(list) {}; + + return this.each(function() { + new $.Autocompleter(this, options); + }); + }, + result: function(handler) { + return this.bind("result", handler); + }, + search: function(handler) { + return this.trigger("search", [handler]); + }, + flushCache: function() { + return this.trigger("flushCache"); + }, + setOptions: function(options){ + return this.trigger("setOptions", [options]); + }, + unautocomplete: function() { + return this.trigger("unautocomplete"); + } +}); + +$.Autocompleter = function(input, options) { + + var KEY = { + UP: 38, + DOWN: 40, + DEL: 46, + TAB: 9, + RETURN: 13, + ESC: 27, + COMMA: 188, + PAGEUP: 33, + PAGEDOWN: 34, + BACKSPACE: 8 + }; + + // Create $ object for input element + var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); + + var timeout; + var previousValue = ""; + var cache = $.Autocompleter.Cache(options); + var hasFocus = 0; + var lastKeyPressCode; + var config = { + mouseDownOnSelect: false + }; + var select = $.Autocompleter.Select(options, input, selectCurrent, config); + + // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all + $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { + // a keypress means the input has focus + // avoids issue where input had focus before the autocomplete was applied + hasFocus = 1; + // track last key pressed + lastKeyPressCode = event.keyCode; + switch(event.keyCode) { + + case KEY.UP: + event.preventDefault(); + if ( select.visible() ) { + select.prev(); + } else { + onChange(0, true); + } + break; + + case KEY.DOWN: + event.preventDefault(); + if ( select.visible() ) { + select.next(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEUP: + event.preventDefault(); + if ( select.visible() ) { + select.pageUp(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEDOWN: + event.preventDefault(); + if ( select.visible() ) { + select.pageDown(); + } else { + onChange(0, true); + } + break; + + // matches also semicolon + case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: + case KEY.TAB: + case KEY.RETURN: + if( selectCurrent() ) { + //event.preventDefault(); + //return false; + } + break; + + case KEY.ESC: + select.hide(); + break; + + default: + clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + if (!config.mouseDownOnSelect) { + hideResults(); + } + }).click(function() { + // show select when clicking in a focused field + if ( hasFocus++ > 1 && !select.visible() ) { + onChange(0, true); + } + }).bind("search", function() { + // TODO why not just specifying both arguments? + var fn = (arguments.length > 1) ? arguments[1] : null; + function findValueCallback(q, data) { + var result; + if( data && data.length ) { + for (var i=0; i < data.length; i++) { + if( data[i].result.toLowerCase() == q.toLowerCase() ) { + result = data[i]; + break; + } + } + } + if( typeof fn == "function" ) fn(result); + else $input.trigger("result", result && [result.data, result.value]); + } + $.each(trimWords($input.val()), function(i, value) { + request(value, findValueCallback, findValueCallback); + }); + }).bind("flushCache", function() { + cache.flush(); + }).bind("setOptions", function() { + $.extend(options, arguments[1]); + // if we've updated the data, repopulate + if ( "data" in arguments[1] ) + cache.populate(); + }).bind("unautocomplete", function() { + select.unbind(); + $input.unbind(); + $(input.form).unbind(".autocomplete"); + }); + + + function selectCurrent() { + var selected = select.selected(); + if( !selected ) + return false; + + var v = selected.result; + previousValue = v; + + if ( options.multiple ) { + var words = trimWords($input.val()); + if ( words.length > 1 ) { + var seperator = options.multipleSeparator.length; + var cursorAt = $(input).selection().start; + var wordAt, progress = 0; + $.each(words, function(i, word) { + progress += word.length; + if (cursorAt <= progress) { + wordAt = i; + return false; + } + progress += seperator; + }); + words[wordAt] = v; + // TODO this should set the cursor to the right position, but it gets overriden somewhere + //$.Autocompleter.Selection(input, progress + seperator, progress + seperator); + v = words.join( options.multipleSeparator ); + } + v += options.multipleSeparator; + } + + $input.val(v); + hideResultsNow(); + $input.trigger("result", [selected.data, selected.value]); + return true; + } + + function onChange(crap, skipPrevCheck) { + if( lastKeyPressCode == KEY.DEL ) { + select.hide(); + return; + } + + var currentValue = $input.val(); + + if ( !skipPrevCheck && currentValue == previousValue ) + return; + + previousValue = currentValue; + + currentValue = lastWord(currentValue); + if ( currentValue.length >= options.minChars) { + $input.addClass(options.loadingClass); + if (!options.matchCase) + currentValue = currentValue.toLowerCase(); + request(currentValue, receiveData, hideResultsNow); + } else { + stopLoading(); + select.hide(); + } + }; + + function trimWords(value) { + if (!value) + return [""]; + if (!options.multiple) + return [$.trim(value)]; + return $.map(value.split(options.multipleSeparator), function(word) { + return $.trim(value).length ? $.trim(word) : null; + }); + } + + function lastWord(value) { + if ( !options.multiple ) + return value; + var words = trimWords(value); + if (words.length == 1) + return words[0]; + var cursorAt = $(input).selection().start; + if (cursorAt == value.length) { + words = trimWords(value) + } else { + words = trimWords(value.replace(value.substring(cursorAt), "")); + } + return words[words.length - 1]; + } + + // fills in the input box w/the first match (assumed to be the best match) + // q: the term entered + // sValue: the first matching result + function autoFill(q, sValue){ + // autofill in the complete box w/the first match as long as the user hasn't entered in more data + // if the last user key pressed was backspace, don't autofill + if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + // fill in the value (keep the case the user has typed) + $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + // select the portion of the value not typed by the user (so the next character will erase) + $(input).selection(previousValue.length, previousValue.length + sValue.length); + } + }; + + function hideResults() { + clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + var wasVisible = select.visible(); + select.hide(); + clearTimeout(timeout); + stopLoading(); + if (options.mustMatch) { + // call search and run callback + $input.search( + function (result){ + // if no value found, clear the input box + if( !result ) { + if (options.multiple) { + var words = trimWords($input.val()).slice(0, -1); + $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); + } + else { + $input.val( "" ); + $input.trigger("result", null); + } + } + } + ); + } + }; + + function receiveData(q, data) { + if ( data && data.length && hasFocus ) { + stopLoading(); + select.display(data, q); + autoFill(q, data[0].value); + select.show(); + } else { + hideResultsNow(); + } + }; + + function request(term, success, failure) { + if (!options.matchCase) + term = term.toLowerCase(); + var data = cache.load(term); + // recieve the cached data + if (data && data.length) { + success(term, data); + // if an AJAX url has been supplied, try loading the data now + } else if( (typeof options.url == "string") && (options.url.length > 0) ){ + + var extraParams = { + timestamp: +new Date() + }; + $.each(options.extraParams, function(key, param) { + extraParams[key] = typeof param == "function" ? param() : param; + }); + + $.ajax({ + // try to leverage ajaxQueue plugin to abort previous requests + mode: "abort", + // limit abortion to this input + port: "autocomplete" + input.name, + dataType: options.dataType, + url: options.url, + data: $.extend({ + q: lastWord(term), + limit: options.max + }, extraParams), + success: function(data) { + var parsed = options.parse && options.parse(data) || parse(data); + cache.add(term, parsed); + success(term, parsed); + } + }); + } else { + // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match + select.emptyList(); + failure(term); + } + }; + + function parse(data) { + var parsed = []; + var rows = data.split("\n"); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: options.formatResult && options.formatResult(row, row[0]) || row[0] + }; + } + } + return parsed; + }; + + function stopLoading() { + $input.removeClass(options.loadingClass); + }; + +}; + +$.Autocompleter.defaults = { + inputClass: "ac_input", + resultsClass: "ac_results", + loadingClass: "ac_loading", + minChars: 1, + delay: 400, + matchCase: false, + matchSubset: true, + matchContains: false, + cacheLength: 10, + max: 100, + mustMatch: false, + extraParams: {}, + selectFirst: true, + formatItem: function(row) { return row[0]; }, + formatMatch: null, + autoFill: false, + width: 0, + multiple: false, + multipleSeparator: ", ", + highlight: function(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + }, + scroll: true, + scrollHeight: 180 +}; + +$.Autocompleter.Cache = function(options) { + + var data = {}; + var length = 0; + + function matchSubset(s, sub) { + if (!options.matchCase) + s = s.toLowerCase(); + var i = s.indexOf(sub); + if (options.matchContains == "word"){ + i = s.toLowerCase().search("\\b" + sub.toLowerCase()); + } + if (i == -1) return false; + return i == 0 || options.matchContains; + }; + + function add(q, value) { + if (length > options.cacheLength){ + flush(); + } + if (!data[q]){ + length++; + } + data[q] = value; + } + + function populate(){ + if( !options.data ) return false; + // track the matches + var stMatchSets = {}, + nullData = 0; + + // no url was specified, we need to adjust the cache length to make sure it fits the local data store + if( !options.url ) options.cacheLength = 1; + + // track all options for minChars = 0 + stMatchSets[""] = []; + + // loop through the array and create a lookup structure + for ( var i = 0, ol = options.data.length; i < ol; i++ ) { + var rawValue = options.data[i]; + // if rawValue is a string, make an array otherwise just reference the array + rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; + + var value = options.formatMatch(rawValue, i+1, options.data.length); + if ( value === false ) + continue; + + var firstChar = value.charAt(0).toLowerCase(); + // if no lookup array for this character exists, look it up now + if( !stMatchSets[firstChar] ) + stMatchSets[firstChar] = []; + + // if the match is a string + var row = { + value: value, + data: rawValue, + result: options.formatResult && options.formatResult(rawValue) || value + }; + + // push the current match into the set list + stMatchSets[firstChar].push(row); + + // keep track of minChars zero items + if ( nullData++ < options.max ) { + stMatchSets[""].push(row); + } + }; + + // add the data items to the cache + $.each(stMatchSets, function(i, value) { + // increase the cache size + options.cacheLength++; + // add to the cache + add(i, value); + }); + } + + // populate any existing data + setTimeout(populate, 25); + + function flush(){ + data = {}; + length = 0; + } + + return { + flush: flush, + add: add, + populate: populate, + load: function(q) { + if (!options.cacheLength || !length) + return null; + /* + * if dealing w/local data and matchContains than we must make sure + * to loop through all the data collections looking for matches + */ + if( !options.url && options.matchContains ){ + // track all matches + var csub = []; + // loop through all the data grids for matches + for( var k in data ){ + // don't search through the stMatchSets[""] (minChars: 0) cache + // this prevents duplicates + if( k.length > 0 ){ + var c = data[k]; + $.each(c, function(i, x) { + // if we've got a match, add it to the array + if (matchSubset(x.value, q)) { + csub.push(x); + } + }); + } + } + return csub; + } else + // if the exact item exists, use it + if (data[q]){ + return data[q]; + } else + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var c = data[q.substr(0, i)]; + if (c) { + var csub = []; + $.each(c, function(i, x) { + if (matchSubset(x.value, q)) { + csub[csub.length] = x; + } + }); + return csub; + } + } + } + return null; + } + }; +}; + +$.Autocompleter.Select = function (options, input, select, config) { + var CLASSES = { + ACTIVE: "ac_over" + }; + + var listItems, + active = -1, + data, + term = "", + needsInit = true, + element, + list; + + // Create results + function init() { + if (!needsInit) + return; + element = $("
") + .hide() + .addClass(options.resultsClass) + .css("position", "absolute") + .appendTo(document.body); + + list = $("
    ").appendTo(element).mouseover( function(event) { + if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { + active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); + $(target(event)).addClass(CLASSES.ACTIVE); + } + }).click(function(event) { + $(target(event)).addClass(CLASSES.ACTIVE); + select(); + // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus + input.focus(); + return false; + }).mousedown(function() { + config.mouseDownOnSelect = true; + }).mouseup(function() { + config.mouseDownOnSelect = false; + }); + + if( options.width > 0 ) + element.css("width", options.width); + + needsInit = false; + } + + function target(event) { + var element = event.target; + while(element && element.tagName != "LI") + element = element.parentNode; + // more fun with IE, sometimes event.target is empty, just ignore it then + if(!element) + return []; + return element; + } + + function moveSelect(step) { + listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); + movePosition(step); + var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); + if(options.scroll) { + var offset = 0; + listItems.slice(0, active).each(function() { + offset += this.offsetHeight; + }); + if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { + list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); + } else if(offset < list.scrollTop()) { + list.scrollTop(offset); + } + } + }; + + function movePosition(step) { + active += step; + if (active < 0) { + active = listItems.size() - 1; + } else if (active >= listItems.size()) { + active = 0; + } + } + + function limitNumberOfItems(available) { + return options.max && options.max < available + ? options.max + : available; + } + + function fillList() { + list.empty(); + var max = limitNumberOfItems(data.length); + for (var i=0; i < max; i++) { + if (!data[i]) + continue; + var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); + if ( formatted === false ) + continue; + var li = $("
  • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; + $.data(li, "ac_data", data[i]); + } + listItems = list.find("li"); + if ( options.selectFirst ) { + listItems.slice(0, 1).addClass(CLASSES.ACTIVE); + active = 0; + } + // apply bgiframe if available + if ( $.fn.bgiframe ) + list.bgiframe(); + } + + return { + display: function(d, q) { + init(); + data = d; + term = q; + fillList(); + }, + next: function() { + moveSelect(1); + }, + prev: function() { + moveSelect(-1); + }, + pageUp: function() { + if (active != 0 && active - 8 < 0) { + moveSelect( -active ); + } else { + moveSelect(-8); + } + }, + pageDown: function() { + if (active != listItems.size() - 1 && active + 8 > listItems.size()) { + moveSelect( listItems.size() - 1 - active ); + } else { + moveSelect(8); + } + }, + hide: function() { + element && element.hide(); + listItems && listItems.removeClass(CLASSES.ACTIVE); + active = -1; + }, + visible : function() { + return element && element.is(":visible"); + }, + current: function() { + return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); + }, + show: function() { + var offset = $(input).offset(); + element.css({ + width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).innerWidth(), + top: offset.top + input.offsetHeight, + left: offset.left + }).show(); + options.show(element); + if(options.scroll) { + list.scrollTop(0); + list.css({ + maxHeight: options.scrollHeight, + overflow: 'auto' + }); + + if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { + var listHeight = 0; + listItems.each(function() { + listHeight += this.offsetHeight; + }); + var scrollbarsVisible = listHeight > options.scrollHeight; + list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); + if (!scrollbarsVisible) { + // IE doesn't recalculate width when scrollbar disappears + listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); + } + } + + } + }, + selected: function() { + var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); + return selected && selected.length && $.data(selected[0], "ac_data"); + }, + emptyList: function (){ + list && list.empty(); + }, + unbind: function() { + element && element.remove(); + } + }; +}; + +$.fn.selection = function(start, end) { + if (start !== undefined) { + return this.each(function() { + if( this.createTextRange ){ + var selRange = this.createTextRange(); + if (end === undefined || start == end) { + selRange.move("character", start); + selRange.select(); + } else { + selRange.collapse(true); + selRange.moveStart("character", start); + selRange.moveEnd("character", end); + selRange.select(); + } + } else if( this.setSelectionRange ){ + this.setSelectionRange(start, end); + } else if( this.selectionStart ){ + this.selectionStart = start; + this.selectionEnd = end; + } + }); + } + var field = this[0]; + if ( field.createTextRange ) { + var range = document.selection.createRange(), + orig = field.value, + teststring = "<->", + textLength = range.text.length; + range.text = teststring; + var caretAt = field.value.indexOf(teststring); + field.value = orig; + this.selection(caretAt, caretAt + textLength); + return { + start: caretAt, + end: caretAt + textLength + } + } else if( field.selectionStart !== undefined ){ + return { + start: field.selectionStart, + end: field.selectionEnd + } + } +}; + +})(jQuery); \ No newline at end of file diff --git a/apigen/templates/woodocs/js/jquery.cookie.js b/apigen/templates/woodocs/js/jquery.cookie.js new file mode 100644 index 00000000000..6df1faca25f --- /dev/null +++ b/apigen/templates/woodocs/js/jquery.cookie.js @@ -0,0 +1,96 @@ +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +}; \ No newline at end of file diff --git a/apigen/templates/woodocs/js/jquery.min.js b/apigen/templates/woodocs/js/jquery.min.js new file mode 100644 index 00000000000..3ca5e0f5dee --- /dev/null +++ b/apigen/templates/woodocs/js/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7 jquery.com | jquery.org/license */ +(function(a,b){function cA(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cx(a){if(!cm[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"":"")+""),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="
    "+""+"
    ";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&& +(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after" +,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/apigen/templates/woodocs/js/jquery.sortElements.js b/apigen/templates/woodocs/js/jquery.sortElements.js new file mode 100644 index 00000000000..61f60de8bef --- /dev/null +++ b/apigen/templates/woodocs/js/jquery.sortElements.js @@ -0,0 +1,69 @@ +/** + * jQuery.fn.sortElements + * -------------- + * @author James Padolsey (http://james.padolsey.com) + * @version 0.11 + * @updated 18-MAR-2010 + * -------------- + * @param Function comparator: + * Exactly the same behaviour as [1,2,3].sort(comparator) + * + * @param Function getSortable + * A function that should return the element that is + * to be sorted. The comparator will run on the + * current collection, but you may want the actual + * resulting sort to occur on a parent or another + * associated element. + * + * E.g. $('td').sortElements(comparator, function(){ + * return this.parentNode; + * }) + * + * The
    ) will be sorted instead + * of the ",""],legend:[1,"
    ","
    "],thead:[1,"
    's parent (
    itself. + */ +jQuery.fn.sortElements = (function(){ + + var sort = [].sort; + + return function(comparator, getSortable) { + + getSortable = getSortable || function(){return this;}; + + var placements = this.map(function(){ + + var sortElement = getSortable.call(this), + parentNode = sortElement.parentNode, + + // Since the element itself will change position, we have + // to have some way of storing it's original position in + // the DOM. The easiest way is to have a 'flag' node: + nextSibling = parentNode.insertBefore( + document.createTextNode(''), + sortElement.nextSibling + ); + + return function() { + + if (parentNode === this) { + throw new Error( + "You can't sort elements if any one is a descendant of another." + ); + } + + // Insert before flag: + parentNode.insertBefore(this, nextSibling); + // Remove flag: + parentNode.removeChild(nextSibling); + + }; + + }); + + return sort.call(this, comparator).each(function(i){ + placements[i].call(getSortable.call(this)); + }); + + }; + +})(); \ No newline at end of file diff --git a/apigen/templates/woodocs/js/jquery.sprintf.js b/apigen/templates/woodocs/js/jquery.sprintf.js new file mode 100644 index 00000000000..2eed7fdd9c2 --- /dev/null +++ b/apigen/templates/woodocs/js/jquery.sprintf.js @@ -0,0 +1,8 @@ +/*! + * sprintf and vsprintf for jQuery + * somewhat based on http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/ + * Copyright (c) 2008 Sabin Iacob (m0n5t3r) + * @license http://www.gnu.org/licenses/gpl.html + * @project jquery.sprintf + */ +(function(d){var a={b:function(e){return parseInt(e,10).toString(2)},c:function(e){return String.fromCharCode(parseInt(e,10))},d:function(e){return parseInt(e,10)},u:function(e){return Math.abs(e)},f:function(f,e){e=parseInt(e,10);f=parseFloat(f);if(isNaN(e&&f)){return NaN}return e&&f.toFixed(e)||f},o:function(e){return parseInt(e,10).toString(8)},s:function(e){return e},x:function(e){return(""+parseInt(e,10).toString(16)).toLowerCase()},X:function(e){return(""+parseInt(e,10).toString(16)).toUpperCase()}};var c=/%(?:(\d+)?(?:\.(\d+))?|\(([^)]+)\))([%bcdufosxX])/g;var b=function(f){if(f.length==1&&typeof f[0]=="object"){f=f[0];return function(i,h,k,j,g,m,l){return a[g](f[j])}}else{var e=0;return function(i,h,k,j,g,m,l){if(g=="%"){return"%"}return a[g](f[e++],k)}}};d.extend({sprintf:function(f){var e=Array.apply(null,arguments).slice(1);return f.replace(c,b(e))},vsprintf:function(f,e){return f.replace(c,b(e))}})})(jQuery); diff --git a/apigen/templates/woodocs/js/main.js b/apigen/templates/woodocs/js/main.js new file mode 100644 index 00000000000..0d3105ffaca --- /dev/null +++ b/apigen/templates/woodocs/js/main.js @@ -0,0 +1,292 @@ +/*! + * ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + * + * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) + * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) + * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + * Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + * + * For the full copyright and license information, please view + * the file LICENSE.md that was distributed with this source code. + */ + +$(function() { + var $document = $(document); + var $navigation = $('#navigation'); + var navigationHeight = $('#navigation').height(); + var $left = $('#left'); + var $right = $('#right'); + var $rightInner = $('#rightInner'); + var $splitter = $('#splitter'); + var $groups = $('#groups'); + var $content = $('#content'); + + // Menu + + // Hide deep packages and namespaces + $('ul span', $groups).click(function(event) { + event.preventDefault(); + event.stopPropagation(); + $(this) + .toggleClass('collapsed') + .parent() + .next('ul') + .toggleClass('collapsed'); + }).click(); + + $active = $('ul li.active', $groups); + if ($active.length > 0) { + // Open active + $('> a > span', $active).click(); + } else { + $main = $('> ul > li.main', $groups); + if ($main.length > 0) { + // Open first level of the main project + $('> a > span', $main).click(); + } else { + // Open first level of all + $('> ul > li > a > span', $groups).click(); + } + } + + // Content + + // Search autocompletion + var autocompleteFound = false; + var autocompleteFiles = {'c': 'class', 'co': 'constant', 'f': 'function', 'm': 'class', 'mm': 'class', 'p': 'class', 'mp': 'class', 'cc': 'class'}; + var $search = $('#search input[name=q]'); + $search + .autocomplete(ApiGen.elements, { + matchContains: true, + scrollHeight: 200, + max: 20, + formatItem: function(data) { + return data[1].replace(/^(.+\\)(.+)$/, '$1$2'); + }, + formatMatch: function(data) { + return data[1]; + }, + formatResult: function(data) { + return data[1]; + }, + show: function($list) { + var $items = $('li span', $list); + var maxWidth = Math.max.apply(null, $items.map(function() { + return $(this).width(); + })); + // 10px padding + $list.width(Math.max(maxWidth + 10, $search.innerWidth())); + } + }).result(function(event, data) { + autocompleteFound = true; + var location = window.location.href.split('/'); + location.pop(); + var parts = data[1].split(/::|$/); + var file = $.sprintf(ApiGen.config.templates.main[autocompleteFiles[data[0]]].filename, parts[0].replace(/[^\w]/g, '.')); + if (parts[1]) { + file += '#' + ('mm' === data[0] || 'mp' === data[0] ? 'm' : '') + parts[1].replace(/([\w]+)\(\)/, '_$1'); + } + location.push(file); + window.location = location.join('/'); + + // Workaround for Opera bug + $(this).closest('form').attr('action', location.join('/')); + }).closest('form') + .submit(function() { + var query = $search.val(); + if ('' === query) { + return false; + } + + var label = $('#search input[name=more]').val(); + if (!autocompleteFound && label && -1 === query.indexOf('more:')) { + $search.val(query + ' more:' + label); + } + + return !autocompleteFound && '' !== $('#search input[name=cx]').val(); + }); + + // Save natural order + $('table.summary tr[data-order]', $content).each(function(index) { + do { + index = '0' + index; + } while (index.length < 3); + $(this).attr('data-order-natural', index); + }); + + // Switch between natural and alphabetical order + var $caption = $('table.summary', $content) + .filter(':has(tr[data-order])') + .prev('h2'); + $caption + .click(function() { + var $this = $(this); + var order = $this.data('order') || 'natural'; + order = 'natural' === order ? 'alphabetical' : 'natural'; + $this.data('order', order); + $.cookie('order', order, {expires: 365}); + var attr = 'alphabetical' === order ? 'data-order' : 'data-order-natural'; + $this + .next('table') + .find('tr').sortElements(function(a, b) { + return $(a).attr(attr) > $(b).attr(attr) ? 1 : -1; + }); + return false; + }) + .addClass('switchable') + .attr('title', 'Switch between natural and alphabetical order'); + if ((null === $.cookie('order') && 'alphabetical' === ApiGen.config.options.elementsOrder) || 'alphabetical' === $.cookie('order')) { + $caption.click(); + } + + // Open details + if (ApiGen.config.options.elementDetailsCollapsed) { + $('tr', $content).filter(':has(.detailed)') + .click(function() { + var $this = $(this); + $('.short', $this).hide(); + $('.detailed', $this).show(); + }); + } + + // Splitter + var splitterWidth = $splitter.width(); + function setSplitterPosition(position) + { + $left.width(position); + $right.css('margin-left', position + splitterWidth); + $splitter.css('left', position); + } + function setNavigationPosition() + { + var height = $(window).height() - navigationHeight; + $left.height(height); + $splitter.height(height); + $right.height(height); + } + function setContentWidth() + { + var width = $rightInner.width(); + $rightInner + .toggleClass('medium', width <= 960) + .toggleClass('small', width <= 650); + } + $splitter.mousedown(function() { + $splitter.addClass('active'); + + $document.mousemove(function(event) { + if (event.pageX >= 230 && $document.width() - event.pageX >= 600 + splitterWidth) { + setSplitterPosition(event.pageX); + setContentWidth(); + } + }); + + $() + .add($splitter) + .add($document) + .mouseup(function() { + $splitter + .removeClass('active') + .unbind('mouseup'); + $document + .unbind('mousemove') + .unbind('mouseup'); + + $.cookie('splitter', parseInt($splitter.css('left')), {expires: 365}); + }); + + return false; + }); + var splitterPosition = $.cookie('splitter'); + if (null !== splitterPosition) { + setSplitterPosition(parseInt(splitterPosition)); + } + setNavigationPosition(); + setContentWidth(); + $(window) + .resize(setNavigationPosition) + .resize(setContentWidth); + + // Select selected lines + var matches = window.location.hash.substr(1).match(/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/); + if (null !== matches) { + var lists = matches[0].split(','); + for (var i = 0; i < lists.length; i++) { + var lines = lists[i].split('-'); + lines[1] = lines[1] || lines[0]; + for (var j = lines[0]; j <= lines[1]; j++) { + $('#' + j).addClass('selected'); + } + } + + var $firstLine = $('#' + parseInt(matches[0])); + if ($firstLine.length > 0) { + $right.scrollTop($firstLine.offset().top); + } + } + + // Save selected lines + var lastLine; + $('a.l').click(function(event) { + event.preventDefault(); + + var $selectedLine = $(this).parent(); + var selectedLine = parseInt($selectedLine.attr('id')); + + if (event.shiftKey) { + if (lastLine) { + for (var i = Math.min(selectedLine, lastLine); i <= Math.max(selectedLine, lastLine); i++) { + $('#' + i).addClass('selected'); + } + } else { + $selectedLine.addClass('selected'); + } + } else if (event.ctrlKey) { + $selectedLine.toggleClass('selected'); + } else { + var $selected = $('.l.selected') + .not($selectedLine) + .removeClass('selected'); + if ($selected.length > 0) { + $selectedLine.addClass('selected'); + } else { + $selectedLine.toggleClass('selected'); + } + } + + lastLine = $selectedLine.hasClass('selected') ? selectedLine : null; + + // Update hash + var lines = $('.l.selected') + .map(function() { + return parseInt($(this).attr('id')); + }) + .get() + .sort(function(a, b) { + return a - b; + }); + + var hash = []; + var list = []; + for (var j = 0; j < lines.length; j++) { + if (0 === j && j + 1 === lines.length) { + hash.push(lines[j]); + } else if (0 === j) { + list[0] = lines[j]; + } else if (lines[j - 1] + 1 !== lines[j] && j + 1 === lines.length) { + hash.push(list.join('-')); + hash.push(lines[j]); + } else if (lines[j - 1] + 1 !== lines[j]) { + hash.push(list.join('-')); + list = [lines[j]]; + } else if (j + 1 === lines.length) { + list[1] = lines[j]; + hash.push(list.join('-')); + } else { + list[1] = lines[j]; + } + } + + window.location.hash = hash.join(','); + }); +}); diff --git a/apigen/templates/woodocs/namespace.latte b/apigen/templates/woodocs/namespace.latte new file mode 100644 index 00000000000..67b745560cc --- /dev/null +++ b/apigen/templates/woodocs/namespace.latte @@ -0,0 +1,32 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'namespace'} + +{block #title}{if $namespace != 'None'}Namespace {$namespace}{else}No namespace{/if}{/block} + +{block #content} +
    +

    {if $namespace != 'None'}Namespace {!$namespace|namespaceLinks:false}{else}No namespace{/if}

    + + {if $subnamespaces} +

    Namespaces summary

    + + + + +
    {$namespace}
    + {/if} + + {include '@elementlist.latte'} +
    +{/block} diff --git a/apigen/templates/woodocs/opensearch.xml.latte b/apigen/templates/woodocs/opensearch.xml.latte new file mode 100644 index 00000000000..ae5dfb9d0bb --- /dev/null +++ b/apigen/templates/woodocs/opensearch.xml.latte @@ -0,0 +1,21 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} + + +{$config->title} +{$config->title} Documentation + +{$config->baseUrl}/favicon.ico +open +UTF-8 +UTF-8 + diff --git a/apigen/templates/woodocs/overview.latte b/apigen/templates/woodocs/overview.latte new file mode 100644 index 00000000000..9e65c747856 --- /dev/null +++ b/apigen/templates/woodocs/overview.latte @@ -0,0 +1,57 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'overview'} + +{block #title}{$config->title ?: 'Overview'}{/block} + +{block #content} +
    +

    {include #title}

    + + {var $group = false} + + {if $namespaces} + {if} +

    Namespaces summary

    + + {foreach $namespaces as $namespace} + {continueIf $config->main && 0 !== strpos($namespace, $config->main)} + + {var $group = true} + + + {/foreach} +
    {$namespace}
    + {/if $iterations} + {/if} + + {if $packages} + {if} +

    Packages summary

    + + {foreach $packages as $package} + {continueIf $config->main && 0 !== strpos($package, $config->main)} + + {var $group = true} + + + {/foreach} +
    {$package}
    + {/if $iterations} + {/if} + + {if !$group} + {include '@elementlist.latte'} + {/if} +
    +{/block} \ No newline at end of file diff --git a/apigen/templates/woodocs/package.latte b/apigen/templates/woodocs/package.latte new file mode 100644 index 00000000000..f0f31c78782 --- /dev/null +++ b/apigen/templates/woodocs/package.latte @@ -0,0 +1,32 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'package'} + +{block #title}{if $package != 'None'}Package {$package}{else}No package{/if}{/block} + +{block #content} +
    +

    {if $package != 'None'}Package {!$package|packageLinks:false}{else}No package{/if}

    + + {if $subpackages} +

    Packages summary

    + + + + +
    {$package}
    + {/if} + + {include '@elementlist.latte'} +
    +{/block} diff --git a/apigen/templates/woodocs/resources/bootstrap.min.css b/apigen/templates/woodocs/resources/bootstrap.min.css new file mode 100644 index 00000000000..c98662bc54e --- /dev/null +++ b/apigen/templates/woodocs/resources/bootstrap.min.css @@ -0,0 +1,632 @@ +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +a:hover,a:active{outline:0;} +sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{max-width:100%;height:auto;border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;} +button,input{*overflow:visible;line-height:normal;} +button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} +.clearfix:after{clear:both;} +body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;} +a{color:#985c81;text-decoration:none;} +a:hover{color:#784966;text-decoration:underline;} +.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} +.row:after{clear:both;} +[class*="span"]{float:left;margin-left:20px;} +.span1{width:60px;} +.span2{width:140px;} +.span3{width:220px;} +.span4{width:300px;} +.span5{width:380px;} +.span6{width:460px;} +.span7{width:540px;} +.span8{width:620px;} +.span9{width:700px;} +.span10{width:780px;} +.span11{width:860px;} +.span12,.container{width:940px;} +.offset1{margin-left:100px;} +.offset2{margin-left:180px;} +.offset3{margin-left:260px;} +.offset4{margin-left:340px;} +.offset5{margin-left:420px;} +.offset6{margin-left:500px;} +.offset7{margin-left:580px;} +.offset8{margin-left:660px;} +.offset9{margin-left:740px;} +.offset10{margin-left:820px;} +.offset11{margin-left:900px;} +.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} +.row-fluid:after{clear:both;} +.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;} +.row-fluid>[class*="span"]:first-child{margin-left:0;} +.row-fluid>.span1{width:6.382978723%;} +.row-fluid>.span2{width:14.89361702%;} +.row-fluid>.span3{width:23.404255317%;} +.row-fluid>.span4{width:31.914893614%;} +.row-fluid>.span5{width:40.425531911%;} +.row-fluid>.span6{width:48.93617020799999%;} +.row-fluid>.span7{width:57.446808505%;} +.row-fluid>.span8{width:65.95744680199999%;} +.row-fluid>.span9{width:74.468085099%;} +.row-fluid>.span10{width:82.97872339599999%;} +.row-fluid>.span11{width:91.489361693%;} +.row-fluid>.span12{width:99.99999998999999%;} +.container{width:940px;margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";} +.container:after{clear:both;} +.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";} +.container-fluid:after{clear:both;} +p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;} +.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;} +h1,h2,h3,h4,h5,h6{margin:0;font-weight:bold;color:#333333;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;} +h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;} +h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;} +h4,h5,h6{line-height:18px;} +h4{font-size:14px;}h4 small{font-size:12px;} +h5{font-size:12px;} +h6{font-size:11px;color:#999999;text-transform:uppercase;} +.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;} +.page-header h1{line-height:1;} +ul,ol{padding:0;margin:0 0 9px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;} +ul.unstyled,ol.unstyled{margin-left:0;list-style:none;} +dl{margin-bottom:18px;} +dt,dd{line-height:18px;} +dt{font-weight:bold;} +dd{margin-left:9px;} +hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;} +strong{font-weight:bold;} +em{font-style:italic;} +.muted{color:#999999;} +abbr{font-size:90%;text-transform:uppercase;border-bottom:1px dotted #ddd;cursor:help;} +blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;} +blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';} +blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;} +small{font-size:100%;} +cite{font-style:normal;} +code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{padding:3px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;} +pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;} +pre code{padding:0;color:inherit;background-color:transparent;border:0;} +.pre-scrollable{max-height:340px;overflow-y:scroll;} +form{margin:0 0 18px;} +fieldset{padding:0;margin:0;border:0;} +legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;} +label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;} +input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;} +label{display:block;margin-bottom:5px;color:#333333;} +input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.uneditable-textarea{width:auto;height:auto;} +label input,label textarea,label select{display:block;} +input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;} +input[type="image"]{border:0;} +input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;} +select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;} +input[type="file"]{line-height:18px \9;} +select{width:220px;background-color:#ffffff;} +select[multiple],select[size]{height:auto;} +input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +textarea{height:auto;} +input[type="hidden"]{display:none;} +.radio,.checkbox{padding-left:18px;} +.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;} +.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;} +.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;} +.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;} +input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;} +input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;} +input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.input-mini{width:60px;} +.input-small{width:90px;} +.input-medium{width:150px;} +.input-large{width:210px;} +.input-xlarge{width:270px;} +.input-xxlarge{width:530px;} +input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;} +input.span1,textarea.span1,.uneditable-input.span1{width:50px;} +input.span2,textarea.span2,.uneditable-input.span2{width:130px;} +input.span3,textarea.span3,.uneditable-input.span3{width:210px;} +input.span4,textarea.span4,.uneditable-input.span4{width:290px;} +input.span5,textarea.span5,.uneditable-input.span5{width:370px;} +input.span6,textarea.span6,.uneditable-input.span6{width:450px;} +input.span7,textarea.span7,.uneditable-input.span7{width:530px;} +input.span8,textarea.span8,.uneditable-input.span8{width:610px;} +input.span9,textarea.span9,.uneditable-input.span9{width:690px;} +input.span10,textarea.span10,.uneditable-input.span10{width:770px;} +input.span11,textarea.span11,.uneditable-input.span11{width:850px;} +input.span12,textarea.span12,.uneditable-input.span12{width:930px;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;} +.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;} +.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;} +.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;} +.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;} +.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;} +.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;} +.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;} +.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;} +.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;} +input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#f5f5f5;border-top:1px solid #ddd;} +.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} +:-moz-placeholder{color:#999999;} +::-webkit-input-placeholder{color:#999999;} +.help-block{display:block;margin-top:5px;margin-bottom:0;color:#999999;} +.help-inline{display:inline-block;*display:inline;*zoom:1;margin-bottom:9px;vertical-align:middle;padding-left:5px;} +.input-prepend,.input-append{margin-bottom:5px;*zoom:1;}.input-prepend:before,.input-append:before,.input-prepend:after,.input-append:after{display:table;content:"";} +.input-prepend:after,.input-append:after{clear:both;} +.input-prepend input,.input-append input,.input-prepend .uneditable-input,.input-append .uneditable-input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;} +.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;} +.input-prepend .add-on,.input-append .add-on{float:left;display:block;width:auto;min-width:16px;height:18px;margin-right:-1px;padding:4px 5px;font-weight:normal;line-height:18px;color:#999999;text-align:center;text-shadow:0 1px 0 #ffffff;background-color:#f5f5f5;border:1px solid #ccc;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;} +.input-prepend .add-on{*margin-top:1px;} +.input-append input,.input-append .uneditable-input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;} +.input-append .add-on{margin-right:0;margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-append input:first-child{*margin-left:-160px;}.input-append input:first-child+.add-on{*margin-left:-21px;} +.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;} +.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input{display:inline-block;margin-bottom:0;} +.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;} +.form-search label,.form-inline label,.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{display:inline-block;} +.form-search .input-append .add-on,.form-inline .input-prepend .add-on,.form-search .input-append .add-on,.form-inline .input-prepend .add-on{vertical-align:middle;} +.form-search .radio,.form-inline .radio,.form-search .checkbox,.form-inline .checkbox{margin-bottom:0;vertical-align:middle;} +.control-group{margin-bottom:9px;} +legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;} +.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";} +.form-horizontal .control-group:after{clear:both;} +.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;} +.form-horizontal .controls{margin-left:160px;} +.form-horizontal .form-actions{padding-left:160px;} +table{max-width:100%;border-collapse:collapse;border-spacing:0;} +.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #ddd;} +.table th{font-weight:bold;} +.table thead th{vertical-align:bottom;} +.table thead:first-child tr th,.table thead:first-child tr td{border-top:0;} +.table tbody+tbody{border-top:2px solid #ddd;} +.table-condensed th,.table-condensed td{padding:4px 5px;} +.table-bordered{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th+th,.table-bordered td+td,.table-bordered th+td,.table-bordered td+th{border-left:1px solid #ddd;} +.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;} +.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} +.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} +.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} +.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} +.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;} +table .span1{float:none;width:44px;margin-left:0;} +table .span2{float:none;width:124px;margin-left:0;} +table .span3{float:none;width:204px;margin-left:0;} +table .span4{float:none;width:284px;margin-left:0;} +table .span5{float:none;width:364px;margin-left:0;} +table .span6{float:none;width:444px;margin-left:0;} +table .span7{float:none;width:524px;margin-left:0;} +table .span8{float:none;width:604px;margin-left:0;} +table .span9{float:none;width:684px;margin-left:0;} +table .span10{float:none;width:764px;margin-left:0;} +table .span11{float:none;width:844px;margin-left:0;} +table .span12{float:none;width:924px;margin-left:0;} +[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;} +.icon-white{background-image:url("../img/glyphicons-halflings-white.png");} +.icon-glass{background-position:0 0;} +.icon-music{background-position:-24px 0;} +.icon-search{background-position:-48px 0;} +.icon-envelope{background-position:-72px 0;} +.icon-heart{background-position:-96px 0;} +.icon-star{background-position:-120px 0;} +.icon-star-empty{background-position:-144px 0;} +.icon-user{background-position:-168px 0;} +.icon-film{background-position:-192px 0;} +.icon-th-large{background-position:-216px 0;} +.icon-th{background-position:-240px 0;} +.icon-th-list{background-position:-264px 0;} +.icon-ok{background-position:-288px 0;} +.icon-remove{background-position:-312px 0;} +.icon-zoom-in{background-position:-336px 0;} +.icon-zoom-out{background-position:-360px 0;} +.icon-off{background-position:-384px 0;} +.icon-signal{background-position:-408px 0;} +.icon-cog{background-position:-432px 0;} +.icon-trash{background-position:-456px 0;} +.icon-home{background-position:0 -24px;} +.icon-file{background-position:-24px -24px;} +.icon-time{background-position:-48px -24px;} +.icon-road{background-position:-72px -24px;} +.icon-download-alt{background-position:-96px -24px;} +.icon-download{background-position:-120px -24px;} +.icon-upload{background-position:-144px -24px;} +.icon-inbox{background-position:-168px -24px;} +.icon-play-circle{background-position:-192px -24px;} +.icon-repeat{background-position:-216px -24px;} +.icon-refresh{background-position:-240px -24px;} +.icon-list-alt{background-position:-264px -24px;} +.icon-lock{background-position:-287px -24px;} +.icon-flag{background-position:-312px -24px;} +.icon-headphones{background-position:-336px -24px;} +.icon-volume-off{background-position:-360px -24px;} +.icon-volume-down{background-position:-384px -24px;} +.icon-volume-up{background-position:-408px -24px;} +.icon-qrcode{background-position:-432px -24px;} +.icon-barcode{background-position:-456px -24px;} +.icon-tag{background-position:0 -48px;} +.icon-tags{background-position:-25px -48px;} +.icon-book{background-position:-48px -48px;} +.icon-bookmark{background-position:-72px -48px;} +.icon-print{background-position:-96px -48px;} +.icon-camera{background-position:-120px -48px;} +.icon-font{background-position:-144px -48px;} +.icon-bold{background-position:-167px -48px;} +.icon-italic{background-position:-192px -48px;} +.icon-text-height{background-position:-216px -48px;} +.icon-text-width{background-position:-240px -48px;} +.icon-align-left{background-position:-264px -48px;} +.icon-align-center{background-position:-288px -48px;} +.icon-align-right{background-position:-312px -48px;} +.icon-align-justify{background-position:-336px -48px;} +.icon-list{background-position:-360px -48px;} +.icon-indent-left{background-position:-384px -48px;} +.icon-indent-right{background-position:-408px -48px;} +.icon-facetime-video{background-position:-432px -48px;} +.icon-picture{background-position:-456px -48px;} +.icon-pencil{background-position:0 -72px;} +.icon-map-marker{background-position:-24px -72px;} +.icon-adjust{background-position:-48px -72px;} +.icon-tint{background-position:-72px -72px;} +.icon-edit{background-position:-96px -72px;} +.icon-share{background-position:-120px -72px;} +.icon-check{background-position:-144px -72px;} +.icon-move{background-position:-168px -72px;} +.icon-step-backward{background-position:-192px -72px;} +.icon-fast-backward{background-position:-216px -72px;} +.icon-backward{background-position:-240px -72px;} +.icon-play{background-position:-264px -72px;} +.icon-pause{background-position:-288px -72px;} +.icon-stop{background-position:-312px -72px;} +.icon-forward{background-position:-336px -72px;} +.icon-fast-forward{background-position:-360px -72px;} +.icon-step-forward{background-position:-384px -72px;} +.icon-eject{background-position:-408px -72px;} +.icon-chevron-left{background-position:-432px -72px;} +.icon-chevron-right{background-position:-456px -72px;} +.icon-plus-sign{background-position:0 -96px;} +.icon-minus-sign{background-position:-24px -96px;} +.icon-remove-sign{background-position:-48px -96px;} +.icon-ok-sign{background-position:-72px -96px;} +.icon-question-sign{background-position:-96px -96px;} +.icon-info-sign{background-position:-120px -96px;} +.icon-screenshot{background-position:-144px -96px;} +.icon-remove-circle{background-position:-168px -96px;} +.icon-ok-circle{background-position:-192px -96px;} +.icon-ban-circle{background-position:-216px -96px;} +.icon-arrow-left{background-position:-240px -96px;} +.icon-arrow-right{background-position:-264px -96px;} +.icon-arrow-up{background-position:-289px -96px;} +.icon-arrow-down{background-position:-312px -96px;} +.icon-share-alt{background-position:-336px -96px;} +.icon-resize-full{background-position:-360px -96px;} +.icon-resize-small{background-position:-384px -96px;} +.icon-plus{background-position:-408px -96px;} +.icon-minus{background-position:-433px -96px;} +.icon-asterisk{background-position:-456px -96px;} +.icon-exclamation-sign{background-position:0 -120px;} +.icon-gift{background-position:-24px -120px;} +.icon-leaf{background-position:-48px -120px;} +.icon-fire{background-position:-72px -120px;} +.icon-eye-open{background-position:-96px -120px;} +.icon-eye-close{background-position:-120px -120px;} +.icon-warning-sign{background-position:-144px -120px;} +.icon-plane{background-position:-168px -120px;} +.icon-calendar{background-position:-192px -120px;} +.icon-random{background-position:-216px -120px;} +.icon-comment{background-position:-240px -120px;} +.icon-magnet{background-position:-264px -120px;} +.icon-chevron-up{background-position:-288px -120px;} +.icon-chevron-down{background-position:-313px -119px;} +.icon-retweet{background-position:-336px -120px;} +.icon-shopping-cart{background-position:-360px -120px;} +.icon-folder-close{background-position:-384px -120px;} +.icon-folder-open{background-position:-408px -120px;} +.icon-resize-vertical{background-position:-432px -119px;} +.icon-resize-horizontal{background-position:-456px -118px;} +.dropdown{position:relative;} +.dropdown-toggle{*margin-bottom:-3px;} +.dropdown-toggle:active,.open .dropdown-toggle{outline:0;} +.caret{display:inline-block;width:0;height:0;text-indent:-99999px;*text-indent:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"\2193";} +.dropdown .caret{margin-top:8px;margin-left:2px;} +.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);} +.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;_width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.bottom-up{top:auto;bottom:100%;margin-bottom:2px;} +.dropdown-menu .divider{height:1px;margin:5px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;} +.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#555555;white-space:nowrap;} +.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;} +.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} +.dropdown.open .dropdown-menu{display:block;} +.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} +.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;} +.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;} +.btn{display:inline-block;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;} +.btn:active,.btn.active{background-color:#cccccc \9;} +.btn:first-child{*margin-left:0;} +.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;} +.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;} +.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;} +.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-large [class^="icon-"]{margin-top:1px;} +.btn-small{padding:5px 9px;font-size:11px;line-height:16px;} +.btn-small [class^="icon-"]{margin-top:-1px;} +.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;} +.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;} +.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-dark.active{color:rgba(255, 255, 255, 0.75);} +.btn-primary{background-color:#006dcc;background-image:-moz-linear-gradient(top, #0088cc, #0044cc);background-image:-ms-linear-gradient(top, #0088cc, #0044cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));background-image:-webkit-linear-gradient(top, #0088cc, #0044cc);background-image:-o-linear-gradient(top, #0088cc, #0044cc);background-image:linear-gradient(top, #0088cc, #0044cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);border-color:#0044cc #0044cc #002a80;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0044cc;} +.btn-primary:active,.btn-primary.active{background-color:#003399 \9;} +.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;} +.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;} +.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;} +.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;} +.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;} +.btn-success:active,.btn-success.active{background-color:#408140 \9;} +.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;} +.btn-info:active,.btn-info.active{background-color:#24748c \9;} +.btn-inverse{background-color:#393939;background-image:-moz-linear-gradient(top, #454545, #262626);background-image:-ms-linear-gradient(top, #454545, #262626);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#454545), to(#262626));background-image:-webkit-linear-gradient(top, #454545, #262626);background-image:-o-linear-gradient(top, #454545, #262626);background-image:linear-gradient(top, #454545, #262626);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#454545', endColorstr='#262626', GradientType=0);border-color:#262626 #262626 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#262626;} +.btn-inverse:active,.btn-inverse.active{background-color:#0c0c0c \9;} +button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;} +button.btn.large,input[type="submit"].btn.large{*padding-top:7px;*padding-bottom:7px;} +button.btn.small,input[type="submit"].btn.small{*padding-top:3px;*padding-bottom:3px;} +.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";} +.btn-group:after{clear:both;} +.btn-group:first-child{*margin-left:0;} +.btn-group+.btn-group{margin-left:5px;} +.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;} +.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;} +.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;} +.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;} +.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;} +.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;} +.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;} +.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:5px;*padding-bottom:5px;} +.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);} +.btn .caret{margin-top:7px;margin-left:0;} +.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);} +.btn-primary .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);} +.btn-small .caret{margin-top:4px;} +.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.alert,.alert-heading{color:#c09853;} +.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;} +.alert-success{background-color:#dff0d8;border-color:#d6e9c6;} +.alert-success,.alert-success .alert-heading{color:#468847;} +.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;} +.alert-danger,.alert-error,.alert-danger .alert-heading,.alert-error .alert-heading{color:#b94a48;} +.alert-info{background-color:#d9edf7;border-color:#bce8f1;} +.alert-info,.alert-info .alert-heading{color:#3a87ad;} +.alert-block{padding-top:14px;padding-bottom:14px;} +.alert-block>p,.alert-block>ul{margin-bottom:0;} +.alert-block p+p{margin-top:5px;} +.nav{margin-left:0;margin-bottom:18px;list-style:none;} +.nav>li>a{display:block;} +.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;} +.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;} +.nav li+.nav-header{margin-top:9px;} +.nav-list{padding-left:14px;padding-right:14px;margin-bottom:0;} +.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.nav-list>li>a{padding:3px 15px;} +.nav-list .active>a,.nav-list .active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;} +.nav-list [class^="icon-"]{margin-right:2px;} +.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";} +.nav-tabs:after,.nav-pills:after{clear:both;} +.nav-tabs>li,.nav-pills>li{float:left;} +.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;} +.nav-tabs{border-bottom:1px solid #ddd;} +.nav-tabs>li{margin-bottom:-1px;} +.nav-tabs>li>a{padding-top:9px;padding-bottom:9px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;} +.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;} +.nav-pills .active>a,.nav-pills .active>a:hover{color:#ffffff;background-color:#0088cc;} +.nav-stacked>li{float:none;} +.nav-stacked>li>a{margin-right:0;} +.nav-tabs.nav-stacked{border-bottom:0;} +.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;} +.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;} +.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;} +.nav-pills.nav-stacked>li>a{margin-bottom:3px;} +.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;} +.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;} +.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;margin-top:6px;} +.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;} +.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;} +.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;} +.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;} +.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;opacity:1;filter:alpha(opacity=100);} +.tabs-stacked .open>a:hover{border-color:#999999;} +.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";} +.tabbable:after{clear:both;} +.tab-content{overflow:hidden;} +.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;} +.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.tabs-below .nav-tabs{border-top:1px solid #ddd;} +.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;} +.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;} +.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;} +.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;} +.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;} +.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;} +.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;} +.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;} +.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;} +.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;} +.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;} +.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;} +.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;} +.navbar{overflow:visible;margin-bottom:18px;} +.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;} +.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;} +.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);} +.btn-navbar .icon-bar+.icon-bar{margin-top:3px;} +.nav-collapse.collapse{height:auto;} +.navbar .brand:hover{text-decoration:none;} +.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;} +.navbar .navbar-text{margin-bottom:0;line-height:40px;color:#999999;}.navbar .navbar-text a:hover{color:#ffffff;background-color:transparent;} +.navbar .btn,.navbar .btn-group{margin-top:5px;} +.navbar .btn-group .btn{margin-top:0;} +.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";} +.navbar-form:after{clear:both;} +.navbar-form input,.navbar-form select{display:inline-block;margin-top:5px;margin-bottom:0;} +.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;} +.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;} +.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;} +.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;color:rgba(255, 255, 255, 0.75);background:#666;background:rgba(255, 255, 255, 0.3);border:1px solid #111;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query :-moz-placeholder{color:#eeeeee;} +.navbar-search .search-query::-webkit-input-placeholder{color:#eeeeee;} +.navbar-search .search-query:hover{color:#ffffff;background-color:#999999;background-color:rgba(255, 255, 255, 0.5);} +.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;} +.navbar-fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;} +.navbar-fixed-top .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;} +.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;} +.navbar .nav.pull-right{float:right;} +.navbar .nav>li{display:block;float:left;} +.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;} +.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;} +.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;} +.navbar .nav.pull-right{margin-left:10px;margin-right:0;} +.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;} +.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;} +.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;} +.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);} +.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;} +.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;} +.navbar .nav.pull-right .dropdown-menu{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before{left:auto;right:12px;} +.navbar .nav.pull-right .dropdown-menu:after{left:auto;right:13px;} +.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#999999;} +.breadcrumb .active a{color:#333333;} +.pagination{height:36px;margin:18px 0;} +.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination li{display:inline;} +.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;} +.pagination a:hover,.pagination .active a{background-color:#f5f5f5;} +.pagination .active a{color:#999999;cursor:default;} +.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;} +.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.pagination-centered{text-align:center;} +.pagination-right{text-align:right;} +.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";} +.pager:after{clear:both;} +.pager li{display:inline;} +.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;} +.pager a:hover{text-decoration:none;background-color:#f5f5f5;} +.pager .next a{float:right;} +.pager .previous a{float:left;} +.modal-open .dropdown-menu{z-index:2050;} +.modal-open .dropdown.open{*z-index:2050;} +.modal-open .popover{z-index:2060;} +.modal-open .tooltip{z-index:2070;} +.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);} +.modal{position:fixed;top:50%;left:50%;z-index:1050;max-height:500px;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;} +.modal-body{padding:15px;} +.modal-body .modal-form{margin-bottom:0;} +.modal-footer{padding:14px 15px 15px;margin-bottom:0;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";} +.modal-footer:after{clear:both;} +.modal-footer .btn{float:right;margin-left:5px;margin-bottom:0;} +.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);} +.tooltip.top{margin-top:-2px;} +.tooltip.right{margin-left:2px;} +.tooltip.bottom{margin-top:2px;} +.tooltip.left{margin-left:-2px;} +.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.tooltip-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;} +.popover.right{margin-left:5px;} +.popover.bottom{margin-top:5px;} +.popover.left{margin-left:-5px;} +.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;} +.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;} +.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";} +.thumbnails:after{clear:both;} +.thumbnails>li{float:left;margin:0 0 18px 20px;} +.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);} +a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} +.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;} +.thumbnail .caption{padding:9px;} +.label{padding:2px 4px 3px;font-size:11.049999999999999px;font-weight:bold;color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +.label:hover{color:#ffffff;text-decoration:none;} +.label-important{background-color:#b94a48;} +.label-important:hover{background-color:#953b39;} +.label-warning{background-color:#f89406;} +.label-warning:hover{background-color:#c67605;} +.label-success{background-color:#468847;} +.label-success:hover{background-color:#356635;} +.label-info{background-color:#3a87ad;} +.label-info:hover{background-color:#2d6987;} +@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;} +.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;} +.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;} +.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);} +.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);} +.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);} +.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);} +.accordion{margin-bottom:18px;} +.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.accordion-heading{border-bottom:0;} +.accordion-heading .accordion-toggle{display:block;padding:8px 15px;} +.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;} +.carousel{position:relative;margin-bottom:18px;line-height:1;} +.carousel-inner{overflow:hidden;width:100%;position:relative;} +.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;} +.carousel .item>img{display:block;line-height:1;} +.carousel .active,.carousel .next,.carousel .prev{display:block;} +.carousel .active{left:0;} +.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;} +.carousel .next{left:100%;} +.carousel .prev{left:-100%;} +.carousel .next.left,.carousel .prev.right{left:0;} +.carousel .active.left{left:-100%;} +.carousel .active.right{left:100%;} +.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;} +.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);} +.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);} +.carousel-caption h4,.carousel-caption p{color:#ffffff;} +.hero-unit{padding:60px;margin-bottom:30px;background-color:#f5f5f5;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.invisible{visibility:hidden;} diff --git a/apigen/templates/woodocs/resources/code-line.png b/apigen/templates/woodocs/resources/code-line.png new file mode 100644 index 0000000000000000000000000000000000000000..42f00b66f6a3e6ff2b38b6507f5a844a4b1a53b6 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^l0a<4!3HE(HWf?*QY^(zo*^7SP{WbZ0pxQQctjR6 zFmMZjFyp1Wb$@_@G9|7NCBgY=CFO}lsSJ)O`AMk?Zka`?<@rU~#R|^BriEJ{n*r6m z^>lFziD=z=UR&spg8<8gbEibqmOC#DFwE#{aC#8xK()uv42=k|Kt#F#64nJ za0`Jj6 z_-iQDutebgnKhBSJ>MF+{9wqM-ne#ta*F5njA!wI^^I!$du~=)f4cYC=D606N3$5> z3;TXekJ;1U7`a-b-u3#IMQi6Djy?4He?_px_3j{Z881?RUpAzA_2+6h2cGI+ZBxvX").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cn||(cn=c.createElement("iframe"),cn.frameBorder=cn.width=cn.height=0),b.appendChild(cn);if(!co||!cn.createElement)co=(cn.contentWindow||cn.contentDocument).document,co.write((c.compatMode==="CSS1Compat"?"":"")+""),co.close();d=co.createElement(a),co.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cn)}cm[a]=e}return cm[a]}function cw(a,b){var c={};f.each(cs.concat.apply([],cs.slice(0,b)),function(){c[this]=a});return c}function cv(){ct=b}function cu(){setTimeout(cv,0);return ct=f.now()}function cl(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ck(){try{return new a.XMLHttpRequest}catch(b){}}function ce(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bB(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function br(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bi,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bq(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bp(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bp)}function bp(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bo(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bn(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bm(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(){return!0}function M(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.add(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return a!=null&&m.test(a)&&!isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
    a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,unknownElems:!!a.getElementsByTagName("nav").length,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",enctype:!!c.createElement("form").enctype,submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.lastChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-999px",top:"-999px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;f(function(){var a,b,d,e,g,h,i=1,j="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",l="visibility:hidden;border:0;",n="style='"+j+"border:5px solid #000;padding:0;'",p="
    "+""+"
    ";m=c.getElementsByTagName("body")[0];!m||(a=c.createElement("div"),a.style.cssText=l+"width:0;height:0;position:static;top:0;margin-top:"+i+"px",m.insertBefore(a,m.firstChild),o=c.createElement("div"),o.style.cssText=j+l,o.innerHTML=p,a.appendChild(o),b=o.firstChild,d=b.firstChild,g=b.nextSibling.firstChild.firstChild,h={doesNotAddBorder:d.offsetTop!==5,doesAddBorderForTableAndCells:g.offsetTop===5},d.style.position="fixed",d.style.top="20px",h.fixedPosition=d.offsetTop===20||d.offsetTop===15,d.style.position=d.style.top="",b.style.overflow="hidden",b.style.position="relative",h.subtractsBorderForOverflowNotVisible=d.offsetTop===-5,h.doesNotIncludeMarginInBodyOffset=m.offsetTop!==i,m.removeChild(a),o=a=null,f.extend(k,h))}),o.innerHTML="",n.removeChild(o),o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[f.expando]:a[f.expando]&&f.expando,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[f.expando]=n=++f.uuid:n=f.expando),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[f.expando]:f.expando;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)?b=b:b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" "));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];if(!arguments.length){if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}return b}e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!a||j===3||j===8||j===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g},removeAttr:function(a,b){var c,d,e,g,h=0;if(a.nodeType===1){d=(b||"").split(p),g=d.length;for(;h=0}})});var z=/\.(.*)$/,A=/^(?:textarea|input|select)$/i,B=/\./g,C=/ /g,D=/[^\w\s.|`]/g,E=/^([^\.]*)?(?:\.(.+))?$/,F=/\bhover(\.\S+)?/,G=/^key/,H=/^(?:mouse|contextmenu)|click/,I=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,J=function(a){var b=I.exec(a);b&& +(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},K=function(a,b){return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||a.id===b[2])&&(!b[3]||b[3].test(a.className))},L=function(a){return f.event.special.hover?a:a.replace(F,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=L(c).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"",(g||!e)&&c.preventDefault();if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,n=null;for(m=e.parentNode;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l=0:t===b&&(t=o[s]=r.quick?K(m,r.quick):f(m).is(s)),t&&q.push(r);q.length&&j.push({elem:m,matches:q})}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),G.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),H.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",Z=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,_=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,ba=/<([\w:]+)/,bb=/
    ","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]},bk=X(c);bj.optgroup=bj.option,bj.tbody=bj.tfoot=bj.colgroup=bj.caption=bj.thead,bj.th=bj.td,f.support.htmlSerialize||(bj._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after" +,arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Z,""):null;if(typeof a=="string"&&!bd.test(a)&&(f.support.leadingWhitespace||!$.test(a))&&!bj[(ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(_,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bn(a,d),e=bo(a),g=bo(d);for(h=0;e[h];++h)g[h]&&bn(e[h],g[h])}if(b){bm(a,d);if(c){e=bo(a),g=bo(d);for(h=0;e[h];++h)bm(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bc.test(k))k=b.createTextNode(k);else{k=k.replace(_,"<$1>");var l=(ba.exec(k)||["",""])[1].toLowerCase(),m=bj[l]||bj._default,n=m[0],o=b.createElement("div");b===c?bk.appendChild(o):X(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=bb.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&$.test(k)&&o.insertBefore(b.createTextNode($.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bt.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bs,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bs.test(g)?g.replace(bs,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bB(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bC=function(a,c){var d,e,g;c=c.replace(bu,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bD=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bv.test(f)&&bw.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bB=bC||bD,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bF=/%20/g,bG=/\[\]$/,bH=/\r?\n/g,bI=/#.*$/,bJ=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bK=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bL=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bM=/^(?:GET|HEAD)$/,bN=/^\/\//,bO=/\?/,bP=/)<[^<]*)*<\/script>/gi,bQ=/^(?:select|textarea)/i,bR=/\s+/,bS=/([?&])_=[^&]*/,bT=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bU=f.fn.load,bV={},bW={},bX,bY,bZ=["*/"]+["*"];try{bX=e.href}catch(b$){bX=c.createElement("a"),bX.href="",bX=bX.href}bY=bT.exec(bX.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bU)return bU.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bP,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bQ.test(this.nodeName)||bK.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bH,"\r\n")}}):{name:b.name,value:c.replace(bH,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?cb(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),cb(a,b);return a},ajaxSettings:{url:bX,isLocal:bL.test(bY[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bZ},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:b_(bV),ajaxTransport:b_(bW),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cd(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=ce(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bJ.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bI,"").replace(bN,bY[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bR),d.crossDomain==null&&(r=bT.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bY[1]&&r[2]==bY[2]&&(r[3]||(r[1]==="http:"?80:443))==(bY[3]||(bY[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),ca(bV,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bM.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bO.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bS,"$1_="+x);d.url=y+(y===d.url?(bO.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bZ+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=ca(bW,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)cc(g,a[g],c,e);return d.join("&").replace(bF,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cf=f.now(),cg=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cf++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cg.test(b.url)||e&&cg.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cg,l),b.url===j&&(e&&(k=k.replace(cg,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ch=a.ActiveXObject?function(){for(var a in cj)cj[a](0,1)}:!1,ci=0,cj;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ck()||cl()}:ck,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ch&&delete cj[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++ci,ch&&(cj||(cj={},f(a).unload(ch)),cj[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cm={},cn,co,cp=/^(?:toggle|show|hide)$/,cq=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cr,cs=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],ct;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cw("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cz.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cz.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cA(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cA(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +}; +/*! + * sprintf and vsprintf for jQuery + * somewhat based on http://jan.moesen.nu/code/javascript/sprintf-and-printf-in-javascript/ + * Copyright (c) 2008 Sabin Iacob (m0n5t3r) + * @license http://www.gnu.org/licenses/gpl.html + * @project jquery.sprintf + */ +(function(d){var a={b:function(e){return parseInt(e,10).toString(2)},c:function(e){return String.fromCharCode(parseInt(e,10))},d:function(e){return parseInt(e,10)},u:function(e){return Math.abs(e)},f:function(f,e){e=parseInt(e,10);f=parseFloat(f);if(isNaN(e&&f)){return NaN}return e&&f.toFixed(e)||f},o:function(e){return parseInt(e,10).toString(8)},s:function(e){return e},x:function(e){return(""+parseInt(e,10).toString(16)).toLowerCase()},X:function(e){return(""+parseInt(e,10).toString(16)).toUpperCase()}};var c=/%(?:(\d+)?(?:\.(\d+))?|\(([^)]+)\))([%bcdufosxX])/g;var b=function(f){if(f.length==1&&typeof f[0]=="object"){f=f[0];return function(i,h,k,j,g,m,l){return a[g](f[j])}}else{var e=0;return function(i,h,k,j,g,m,l){if(g=="%"){return"%"}return a[g](f[e++],k)}}};d.extend({sprintf:function(f){var e=Array.apply(null,arguments).slice(1);return f.replace(c,b(e))},vsprintf:function(f,e){return f.replace(c,b(e))}})})(jQuery); + +/*! + * jQuery Autocomplete plugin 1.1 + * + * Copyright (c) 2009 Jörn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $ + */ + +;(function($) { + +$.fn.extend({ + autocomplete: function(urlOrData, options) { + var isUrl = typeof urlOrData == "string"; + options = $.extend({}, $.Autocompleter.defaults, { + url: isUrl ? urlOrData : null, + data: isUrl ? null : urlOrData, + delay: isUrl ? $.Autocompleter.defaults.delay : 10, + max: options && !options.scroll ? 10 : 150 + }, options); + + // if highlight is set to false, replace it with a do-nothing function + options.highlight = options.highlight || function(value) { return value; }; + + // if the formatMatch option is not specified, then use formatItem for backwards compatibility + options.formatMatch = options.formatMatch || options.formatItem; + + options.show = options.show || function(list) {}; + + return this.each(function() { + new $.Autocompleter(this, options); + }); + }, + result: function(handler) { + return this.bind("result", handler); + }, + search: function(handler) { + return this.trigger("search", [handler]); + }, + flushCache: function() { + return this.trigger("flushCache"); + }, + setOptions: function(options){ + return this.trigger("setOptions", [options]); + }, + unautocomplete: function() { + return this.trigger("unautocomplete"); + } +}); + +$.Autocompleter = function(input, options) { + + var KEY = { + UP: 38, + DOWN: 40, + DEL: 46, + TAB: 9, + RETURN: 13, + ESC: 27, + COMMA: 188, + PAGEUP: 33, + PAGEDOWN: 34, + BACKSPACE: 8 + }; + + // Create $ object for input element + var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); + + var timeout; + var previousValue = ""; + var cache = $.Autocompleter.Cache(options); + var hasFocus = 0; + var lastKeyPressCode; + var config = { + mouseDownOnSelect: false + }; + var select = $.Autocompleter.Select(options, input, selectCurrent, config); + + // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all + $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { + // a keypress means the input has focus + // avoids issue where input had focus before the autocomplete was applied + hasFocus = 1; + // track last key pressed + lastKeyPressCode = event.keyCode; + switch(event.keyCode) { + + case KEY.UP: + event.preventDefault(); + if ( select.visible() ) { + select.prev(); + } else { + onChange(0, true); + } + break; + + case KEY.DOWN: + event.preventDefault(); + if ( select.visible() ) { + select.next(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEUP: + event.preventDefault(); + if ( select.visible() ) { + select.pageUp(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEDOWN: + event.preventDefault(); + if ( select.visible() ) { + select.pageDown(); + } else { + onChange(0, true); + } + break; + + // matches also semicolon + case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: + case KEY.TAB: + case KEY.RETURN: + if( selectCurrent() ) { + //event.preventDefault(); + //return false; + } + break; + + case KEY.ESC: + select.hide(); + break; + + default: + clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + if (!config.mouseDownOnSelect) { + hideResults(); + } + }).click(function() { + // show select when clicking in a focused field + if ( hasFocus++ > 1 && !select.visible() ) { + onChange(0, true); + } + }).bind("search", function() { + // TODO why not just specifying both arguments? + var fn = (arguments.length > 1) ? arguments[1] : null; + function findValueCallback(q, data) { + var result; + if( data && data.length ) { + for (var i=0; i < data.length; i++) { + if( data[i].result.toLowerCase() == q.toLowerCase() ) { + result = data[i]; + break; + } + } + } + if( typeof fn == "function" ) fn(result); + else $input.trigger("result", result && [result.data, result.value]); + } + $.each(trimWords($input.val()), function(i, value) { + request(value, findValueCallback, findValueCallback); + }); + }).bind("flushCache", function() { + cache.flush(); + }).bind("setOptions", function() { + $.extend(options, arguments[1]); + // if we've updated the data, repopulate + if ( "data" in arguments[1] ) + cache.populate(); + }).bind("unautocomplete", function() { + select.unbind(); + $input.unbind(); + $(input.form).unbind(".autocomplete"); + }); + + + function selectCurrent() { + var selected = select.selected(); + if( !selected ) + return false; + + var v = selected.result; + previousValue = v; + + if ( options.multiple ) { + var words = trimWords($input.val()); + if ( words.length > 1 ) { + var seperator = options.multipleSeparator.length; + var cursorAt = $(input).selection().start; + var wordAt, progress = 0; + $.each(words, function(i, word) { + progress += word.length; + if (cursorAt <= progress) { + wordAt = i; + return false; + } + progress += seperator; + }); + words[wordAt] = v; + // TODO this should set the cursor to the right position, but it gets overriden somewhere + //$.Autocompleter.Selection(input, progress + seperator, progress + seperator); + v = words.join( options.multipleSeparator ); + } + v += options.multipleSeparator; + } + + $input.val(v); + hideResultsNow(); + $input.trigger("result", [selected.data, selected.value]); + return true; + } + + function onChange(crap, skipPrevCheck) { + if( lastKeyPressCode == KEY.DEL ) { + select.hide(); + return; + } + + var currentValue = $input.val(); + + if ( !skipPrevCheck && currentValue == previousValue ) + return; + + previousValue = currentValue; + + currentValue = lastWord(currentValue); + if ( currentValue.length >= options.minChars) { + $input.addClass(options.loadingClass); + if (!options.matchCase) + currentValue = currentValue.toLowerCase(); + request(currentValue, receiveData, hideResultsNow); + } else { + stopLoading(); + select.hide(); + } + }; + + function trimWords(value) { + if (!value) + return [""]; + if (!options.multiple) + return [$.trim(value)]; + return $.map(value.split(options.multipleSeparator), function(word) { + return $.trim(value).length ? $.trim(word) : null; + }); + } + + function lastWord(value) { + if ( !options.multiple ) + return value; + var words = trimWords(value); + if (words.length == 1) + return words[0]; + var cursorAt = $(input).selection().start; + if (cursorAt == value.length) { + words = trimWords(value) + } else { + words = trimWords(value.replace(value.substring(cursorAt), "")); + } + return words[words.length - 1]; + } + + // fills in the input box w/the first match (assumed to be the best match) + // q: the term entered + // sValue: the first matching result + function autoFill(q, sValue){ + // autofill in the complete box w/the first match as long as the user hasn't entered in more data + // if the last user key pressed was backspace, don't autofill + if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + // fill in the value (keep the case the user has typed) + $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + // select the portion of the value not typed by the user (so the next character will erase) + $(input).selection(previousValue.length, previousValue.length + sValue.length); + } + }; + + function hideResults() { + clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + var wasVisible = select.visible(); + select.hide(); + clearTimeout(timeout); + stopLoading(); + if (options.mustMatch) { + // call search and run callback + $input.search( + function (result){ + // if no value found, clear the input box + if( !result ) { + if (options.multiple) { + var words = trimWords($input.val()).slice(0, -1); + $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); + } + else { + $input.val( "" ); + $input.trigger("result", null); + } + } + } + ); + } + }; + + function receiveData(q, data) { + if ( data && data.length && hasFocus ) { + stopLoading(); + select.display(data, q); + autoFill(q, data[0].value); + select.show(); + } else { + hideResultsNow(); + } + }; + + function request(term, success, failure) { + if (!options.matchCase) + term = term.toLowerCase(); + var data = cache.load(term); + // recieve the cached data + if (data && data.length) { + success(term, data); + // if an AJAX url has been supplied, try loading the data now + } else if( (typeof options.url == "string") && (options.url.length > 0) ){ + + var extraParams = { + timestamp: +new Date() + }; + $.each(options.extraParams, function(key, param) { + extraParams[key] = typeof param == "function" ? param() : param; + }); + + $.ajax({ + // try to leverage ajaxQueue plugin to abort previous requests + mode: "abort", + // limit abortion to this input + port: "autocomplete" + input.name, + dataType: options.dataType, + url: options.url, + data: $.extend({ + q: lastWord(term), + limit: options.max + }, extraParams), + success: function(data) { + var parsed = options.parse && options.parse(data) || parse(data); + cache.add(term, parsed); + success(term, parsed); + } + }); + } else { + // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match + select.emptyList(); + failure(term); + } + }; + + function parse(data) { + var parsed = []; + var rows = data.split("\n"); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: options.formatResult && options.formatResult(row, row[0]) || row[0] + }; + } + } + return parsed; + }; + + function stopLoading() { + $input.removeClass(options.loadingClass); + }; + +}; + +$.Autocompleter.defaults = { + inputClass: "ac_input", + resultsClass: "ac_results", + loadingClass: "ac_loading", + minChars: 1, + delay: 400, + matchCase: false, + matchSubset: true, + matchContains: false, + cacheLength: 10, + max: 100, + mustMatch: false, + extraParams: {}, + selectFirst: true, + formatItem: function(row) { return row[0]; }, + formatMatch: null, + autoFill: false, + width: 0, + multiple: false, + multipleSeparator: ", ", + highlight: function(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + }, + scroll: true, + scrollHeight: 180 +}; + +$.Autocompleter.Cache = function(options) { + + var data = {}; + var length = 0; + + function matchSubset(s, sub) { + if (!options.matchCase) + s = s.toLowerCase(); + var i = s.indexOf(sub); + if (options.matchContains == "word"){ + i = s.toLowerCase().search("\\b" + sub.toLowerCase()); + } + if (i == -1) return false; + return i == 0 || options.matchContains; + }; + + function add(q, value) { + if (length > options.cacheLength){ + flush(); + } + if (!data[q]){ + length++; + } + data[q] = value; + } + + function populate(){ + if( !options.data ) return false; + // track the matches + var stMatchSets = {}, + nullData = 0; + + // no url was specified, we need to adjust the cache length to make sure it fits the local data store + if( !options.url ) options.cacheLength = 1; + + // track all options for minChars = 0 + stMatchSets[""] = []; + + // loop through the array and create a lookup structure + for ( var i = 0, ol = options.data.length; i < ol; i++ ) { + var rawValue = options.data[i]; + // if rawValue is a string, make an array otherwise just reference the array + rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; + + var value = options.formatMatch(rawValue, i+1, options.data.length); + if ( value === false ) + continue; + + var firstChar = value.charAt(0).toLowerCase(); + // if no lookup array for this character exists, look it up now + if( !stMatchSets[firstChar] ) + stMatchSets[firstChar] = []; + + // if the match is a string + var row = { + value: value, + data: rawValue, + result: options.formatResult && options.formatResult(rawValue) || value + }; + + // push the current match into the set list + stMatchSets[firstChar].push(row); + + // keep track of minChars zero items + if ( nullData++ < options.max ) { + stMatchSets[""].push(row); + } + }; + + // add the data items to the cache + $.each(stMatchSets, function(i, value) { + // increase the cache size + options.cacheLength++; + // add to the cache + add(i, value); + }); + } + + // populate any existing data + setTimeout(populate, 25); + + function flush(){ + data = {}; + length = 0; + } + + return { + flush: flush, + add: add, + populate: populate, + load: function(q) { + if (!options.cacheLength || !length) + return null; + /* + * if dealing w/local data and matchContains than we must make sure + * to loop through all the data collections looking for matches + */ + if( !options.url && options.matchContains ){ + // track all matches + var csub = []; + // loop through all the data grids for matches + for( var k in data ){ + // don't search through the stMatchSets[""] (minChars: 0) cache + // this prevents duplicates + if( k.length > 0 ){ + var c = data[k]; + $.each(c, function(i, x) { + // if we've got a match, add it to the array + if (matchSubset(x.value, q)) { + csub.push(x); + } + }); + } + } + return csub; + } else + // if the exact item exists, use it + if (data[q]){ + return data[q]; + } else + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var c = data[q.substr(0, i)]; + if (c) { + var csub = []; + $.each(c, function(i, x) { + if (matchSubset(x.value, q)) { + csub[csub.length] = x; + } + }); + return csub; + } + } + } + return null; + } + }; +}; + +$.Autocompleter.Select = function (options, input, select, config) { + var CLASSES = { + ACTIVE: "ac_over" + }; + + var listItems, + active = -1, + data, + term = "", + needsInit = true, + element, + list; + + // Create results + function init() { + if (!needsInit) + return; + element = $("
    ") + .hide() + .addClass(options.resultsClass) + .css("position", "absolute") + .appendTo(document.body); + + list = $("
      ").appendTo(element).mouseover( function(event) { + if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { + active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); + $(target(event)).addClass(CLASSES.ACTIVE); + } + }).click(function(event) { + $(target(event)).addClass(CLASSES.ACTIVE); + select(); + // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus + input.focus(); + return false; + }).mousedown(function() { + config.mouseDownOnSelect = true; + }).mouseup(function() { + config.mouseDownOnSelect = false; + }); + + if( options.width > 0 ) + element.css("width", options.width); + + needsInit = false; + } + + function target(event) { + var element = event.target; + while(element && element.tagName != "LI") + element = element.parentNode; + // more fun with IE, sometimes event.target is empty, just ignore it then + if(!element) + return []; + return element; + } + + function moveSelect(step) { + listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); + movePosition(step); + var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); + if(options.scroll) { + var offset = 0; + listItems.slice(0, active).each(function() { + offset += this.offsetHeight; + }); + if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { + list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); + } else if(offset < list.scrollTop()) { + list.scrollTop(offset); + } + } + }; + + function movePosition(step) { + active += step; + if (active < 0) { + active = listItems.size() - 1; + } else if (active >= listItems.size()) { + active = 0; + } + } + + function limitNumberOfItems(available) { + return options.max && options.max < available + ? options.max + : available; + } + + function fillList() { + list.empty(); + var max = limitNumberOfItems(data.length); + for (var i=0; i < max; i++) { + if (!data[i]) + continue; + var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); + if ( formatted === false ) + continue; + var li = $("
    • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; + $.data(li, "ac_data", data[i]); + } + listItems = list.find("li"); + if ( options.selectFirst ) { + listItems.slice(0, 1).addClass(CLASSES.ACTIVE); + active = 0; + } + // apply bgiframe if available + if ( $.fn.bgiframe ) + list.bgiframe(); + } + + return { + display: function(d, q) { + init(); + data = d; + term = q; + fillList(); + }, + next: function() { + moveSelect(1); + }, + prev: function() { + moveSelect(-1); + }, + pageUp: function() { + if (active != 0 && active - 8 < 0) { + moveSelect( -active ); + } else { + moveSelect(-8); + } + }, + pageDown: function() { + if (active != listItems.size() - 1 && active + 8 > listItems.size()) { + moveSelect( listItems.size() - 1 - active ); + } else { + moveSelect(8); + } + }, + hide: function() { + element && element.hide(); + listItems && listItems.removeClass(CLASSES.ACTIVE); + active = -1; + }, + visible : function() { + return element && element.is(":visible"); + }, + current: function() { + return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); + }, + show: function() { + var offset = $(input).offset(); + element.css({ + width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).innerWidth(), + top: offset.top + input.offsetHeight, + left: offset.left + }).show(); + options.show(element); + if(options.scroll) { + list.scrollTop(0); + list.css({ + maxHeight: options.scrollHeight, + overflow: 'auto' + }); + + if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { + var listHeight = 0; + listItems.each(function() { + listHeight += this.offsetHeight; + }); + var scrollbarsVisible = listHeight > options.scrollHeight; + list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); + if (!scrollbarsVisible) { + // IE doesn't recalculate width when scrollbar disappears + listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); + } + } + + } + }, + selected: function() { + var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); + return selected && selected.length && $.data(selected[0], "ac_data"); + }, + emptyList: function (){ + list && list.empty(); + }, + unbind: function() { + element && element.remove(); + } + }; +}; + +$.fn.selection = function(start, end) { + if (start !== undefined) { + return this.each(function() { + if( this.createTextRange ){ + var selRange = this.createTextRange(); + if (end === undefined || start == end) { + selRange.move("character", start); + selRange.select(); + } else { + selRange.collapse(true); + selRange.moveStart("character", start); + selRange.moveEnd("character", end); + selRange.select(); + } + } else if( this.setSelectionRange ){ + this.setSelectionRange(start, end); + } else if( this.selectionStart ){ + this.selectionStart = start; + this.selectionEnd = end; + } + }); + } + var field = this[0]; + if ( field.createTextRange ) { + var range = document.selection.createRange(), + orig = field.value, + teststring = "<->", + textLength = range.text.length; + range.text = teststring; + var caretAt = field.value.indexOf(teststring); + field.value = orig; + this.selection(caretAt, caretAt + textLength); + return { + start: caretAt, + end: caretAt + textLength + } + } else if( field.selectionStart !== undefined ){ + return { + start: field.selectionStart, + end: field.selectionEnd + } + } +}; + +})(jQuery); +/** + * jQuery.fn.sortElements + * -------------- + * @author James Padolsey (http://james.padolsey.com) + * @version 0.11 + * @updated 18-MAR-2010 + * -------------- + * @param Function comparator: + * Exactly the same behaviour as [1,2,3].sort(comparator) + * + * @param Function getSortable + * A function that should return the element that is + * to be sorted. The comparator will run on the + * current collection, but you may want the actual + * resulting sort to occur on a parent or another + * associated element. + * + * E.g. $('td').sortElements(comparator, function(){ + * return this.parentNode; + * }) + * + * The
    ) will be sorted instead + * of the + + {foreach $class->annotations['todo'] as $description} + {sep}{/sep} + {/foreach} + + {/foreach} + {/define} + + {if $todoClasses} +

    Classes summary

    +
    's parent (
    itself. + */ +jQuery.fn.sortElements = (function(){ + + var sort = [].sort; + + return function(comparator, getSortable) { + + getSortable = getSortable || function(){return this;}; + + var placements = this.map(function(){ + + var sortElement = getSortable.call(this), + parentNode = sortElement.parentNode, + + // Since the element itself will change position, we have + // to have some way of storing it's original position in + // the DOM. The easiest way is to have a 'flag' node: + nextSibling = parentNode.insertBefore( + document.createTextNode(''), + sortElement.nextSibling + ); + + return function() { + + if (parentNode === this) { + throw new Error( + "You can't sort elements if any one is a descendant of another." + ); + } + + // Insert before flag: + parentNode.insertBefore(this, nextSibling); + // Remove flag: + parentNode.removeChild(nextSibling); + + }; + + }); + + return sort.call(this, comparator).each(function(i){ + placements[i].call(getSortable.call(this)); + }); + + }; + +})(); +/*! + * ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + * + * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) + * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) + * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + * Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + * + * For the full copyright and license information, please view + * the file LICENSE.md that was distributed with this source code. + */ + +$(function() { + var $document = $(document); + var $navigation = $('#navigation'); + var navigationHeight = $('#navigation').height(); + var $left = $('#left'); + var $right = $('#right'); + var $rightInner = $('#rightInner'); + var $splitter = $('#splitter'); + var $groups = $('#groups'); + var $content = $('#content'); + + // Menu + + // Hide deep packages and namespaces + $('ul span', $groups).click(function(event) { + event.preventDefault(); + event.stopPropagation(); + $(this) + .toggleClass('collapsed') + .parent() + .next('ul') + .toggleClass('collapsed'); + }).click(); + + $active = $('ul li.active', $groups); + if ($active.length > 0) { + // Open active + $('> a > span', $active).click(); + } else { + $main = $('> ul > li.main', $groups); + if ($main.length > 0) { + // Open first level of the main project + $('> a > span', $main).click(); + } else { + // Open first level of all + $('> ul > li > a > span', $groups).click(); + } + } + + // Content + + // Search autocompletion + var autocompleteFound = false; + var autocompleteFiles = {'c': 'class', 'co': 'constant', 'f': 'function', 'm': 'class', 'mm': 'class', 'p': 'class', 'mp': 'class', 'cc': 'class'}; + var $search = $('#search input[name=q]'); + $search + .autocomplete(ApiGen.elements, { + matchContains: true, + scrollHeight: 200, + max: 20, + formatItem: function(data) { + return data[1].replace(/^(.+\\)(.+)$/, '$1$2'); + }, + formatMatch: function(data) { + return data[1]; + }, + formatResult: function(data) { + return data[1]; + }, + show: function($list) { + var $items = $('li span', $list); + var maxWidth = Math.max.apply(null, $items.map(function() { + return $(this).width(); + })); + // 10px padding + $list.width(Math.max(maxWidth + 10, $search.innerWidth())); + } + }).result(function(event, data) { + autocompleteFound = true; + var location = window.location.href.split('/'); + location.pop(); + var parts = data[1].split(/::|$/); + var file = $.sprintf(ApiGen.config.templates.main[autocompleteFiles[data[0]]].filename, parts[0].replace(/[^\w]/g, '.')); + if (parts[1]) { + file += '#' + ('mm' === data[0] || 'mp' === data[0] ? 'm' : '') + parts[1].replace(/([\w]+)\(\)/, '_$1'); + } + location.push(file); + window.location = location.join('/'); + + // Workaround for Opera bug + $(this).closest('form').attr('action', location.join('/')); + }).closest('form') + .submit(function() { + var query = $search.val(); + if ('' === query) { + return false; + } + + var label = $('#search input[name=more]').val(); + if (!autocompleteFound && label && -1 === query.indexOf('more:')) { + $search.val(query + ' more:' + label); + } + + return !autocompleteFound && '' !== $('#search input[name=cx]').val(); + }); + + // Save natural order + $('table.summary tr[data-order]', $content).each(function(index) { + do { + index = '0' + index; + } while (index.length < 3); + $(this).attr('data-order-natural', index); + }); + + // Switch between natural and alphabetical order + var $caption = $('table.summary', $content) + .filter(':has(tr[data-order])') + .prev('h2'); + $caption + .click(function() { + var $this = $(this); + var order = $this.data('order') || 'natural'; + order = 'natural' === order ? 'alphabetical' : 'natural'; + $this.data('order', order); + $.cookie('order', order, {expires: 365}); + var attr = 'alphabetical' === order ? 'data-order' : 'data-order-natural'; + $this + .next('table') + .find('tr').sortElements(function(a, b) { + return $(a).attr(attr) > $(b).attr(attr) ? 1 : -1; + }); + return false; + }) + .addClass('switchable') + .attr('title', 'Switch between natural and alphabetical order'); + if ((null === $.cookie('order') && 'alphabetical' === ApiGen.config.options.elementsOrder) || 'alphabetical' === $.cookie('order')) { + $caption.click(); + } + + // Open details + if (ApiGen.config.options.elementDetailsCollapsed) { + $('tr', $content).filter(':has(.detailed)') + .click(function() { + var $this = $(this); + $('.short', $this).hide(); + $('.detailed', $this).show(); + }); + } + + // Splitter + var splitterWidth = $splitter.width(); + function setSplitterPosition(position) + { + $left.width(position); + $right.css('margin-left', position + splitterWidth); + $splitter.css('left', position); + } + function setNavigationPosition() + { + var height = $(window).height() - navigationHeight; + $left.height(height); + $splitter.height(height); + $right.height(height); + } + function setContentWidth() + { + var width = $rightInner.width(); + $rightInner + .toggleClass('medium', width <= 960) + .toggleClass('small', width <= 650); + } + $splitter.mousedown(function() { + $splitter.addClass('active'); + + $document.mousemove(function(event) { + if (event.pageX >= 230 && $document.width() - event.pageX >= 600 + splitterWidth) { + setSplitterPosition(event.pageX); + setContentWidth(); + } + }); + + $() + .add($splitter) + .add($document) + .mouseup(function() { + $splitter + .removeClass('active') + .unbind('mouseup'); + $document + .unbind('mousemove') + .unbind('mouseup'); + + $.cookie('splitter', parseInt($splitter.css('left')), {expires: 365}); + }); + + return false; + }); + var splitterPosition = $.cookie('splitter'); + if (null !== splitterPosition) { + setSplitterPosition(parseInt(splitterPosition)); + } + setNavigationPosition(); + setContentWidth(); + $(window) + .resize(setNavigationPosition) + .resize(setContentWidth); + + // Select selected lines + var matches = window.location.hash.substr(1).match(/^\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*$/); + if (null !== matches) { + var lists = matches[0].split(','); + for (var i = 0; i < lists.length; i++) { + var lines = lists[i].split('-'); + lines[1] = lines[1] || lines[0]; + for (var j = lines[0]; j <= lines[1]; j++) { + $('#' + j).addClass('selected'); + } + } + + var $firstLine = $('#' + parseInt(matches[0])); + if ($firstLine.length > 0) { + $right.scrollTop($firstLine.offset().top); + } + } + + // Save selected lines + var lastLine; + $('a.l').click(function(event) { + event.preventDefault(); + + var $selectedLine = $(this).parent(); + var selectedLine = parseInt($selectedLine.attr('id')); + + if (event.shiftKey) { + if (lastLine) { + for (var i = Math.min(selectedLine, lastLine); i <= Math.max(selectedLine, lastLine); i++) { + $('#' + i).addClass('selected'); + } + } else { + $selectedLine.addClass('selected'); + } + } else if (event.ctrlKey) { + $selectedLine.toggleClass('selected'); + } else { + var $selected = $('.l.selected') + .not($selectedLine) + .removeClass('selected'); + if ($selected.length > 0) { + $selectedLine.addClass('selected'); + } else { + $selectedLine.toggleClass('selected'); + } + } + + lastLine = $selectedLine.hasClass('selected') ? selectedLine : null; + + // Update hash + var lines = $('.l.selected') + .map(function() { + return parseInt($(this).attr('id')); + }) + .get() + .sort(function(a, b) { + return a - b; + }); + + var hash = []; + var list = []; + for (var j = 0; j < lines.length; j++) { + if (0 === j && j + 1 === lines.length) { + hash.push(lines[j]); + } else if (0 === j) { + list[0] = lines[j]; + } else if (lines[j - 1] + 1 !== lines[j] && j + 1 === lines.length) { + hash.push(list.join('-')); + hash.push(lines[j]); + } else if (lines[j - 1] + 1 !== lines[j]) { + hash.push(list.join('-')); + list = [lines[j]]; + } else if (j + 1 === lines.length) { + list[1] = lines[j]; + hash.push(list.join('-')); + } else { + list[1] = lines[j]; + } + } + + window.location.hash = hash.join(','); + }); +}); + diff --git a/apigen/templates/woodocs/resources/inherit.png b/apigen/templates/woodocs/resources/inherit.png new file mode 100644 index 0000000000000000000000000000000000000000..957079b84e49bf180e0b601840ddcde0108e181e GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngd!3-pihitnGq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~-c6|n{Qgt!7}AXu8d^E{AZDhcun{?G7qyMY&wC*|ql7*cU7 rDZz!6$HC&nPY#A>i+9V5GO$Q6gdSwr_d2+Y5v14C)z4*}Q$iB}Nwq1B literal 0 HcmV?d00001 diff --git a/apigen/templates/woodocs/resources/resize.png b/apigen/templates/woodocs/resources/resize.png new file mode 100644 index 0000000000000000000000000000000000000000..fb98a7a3c83122d833ce1f2987e049d946b8f4dd GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1k#0(_c5A*Q?Ddu7)&kzm{j@u9YAAsZnd_r9R z!$6S?=U@}IO_vt{)ro=BITxiSmSpDVDTHL^rZN~9>l>Ks8yepE rcVse91+S-zV+hC0|6H_V+Po~-c6|n{Qgt#i}8UUGZRqr|jDW;Mjzu^B2Pq!O*0eQNfE{-7; zw~`WCnAviC*x1;70@>J964_E5cNk1c?3wZGQ9$?A7@0hWL?#vq2HRSOdDkUx&jM;> N@O1TaS?83{1OS>JFbMzv literal 0 HcmV?d00001 diff --git a/apigen/templates/woodocs/resources/style.css b/apigen/templates/woodocs/resources/style.css new file mode 100644 index 00000000000..2aecb5cbb97 --- /dev/null +++ b/apigen/templates/woodocs/resources/style.css @@ -0,0 +1,488 @@ +/*! + * ApiGen 2.7.0 - API documentation generator for PHP 5.3+ + * + * Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) + * Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) + * Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + * Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + * + * For the full copyright and license information, please view + * the file LICENSE.md that was distributed with this source code. + */ + +body { + padding: 40px 0 0 0; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +h2 { + font-size: 1.5em; + margin: 0.83em 0; +} + +h3 { + font-size: 1.17em; + margin: 1em 0 0.2em 0; +} + +h4 { + font-size: 100%; + margin: 0; + padding: 0; +} + +ul, ol { + margin-bottom: 0; +} + +a, a:hover { + text-decoration: none; +} + +var { + font-weight: bold; + font-style: normal; + color: #c09853; +} + +code { + color: #000000; + white-space: nowrap; +} + +code:empty { + display: none; +} + +code code { + border: none; + padding: 0; +} + +code a b { + color: #000000; +} + +pre code { + white-space: pre; +} + +.deprecated { + text-decoration: line-through; +} + +.invalid { + color: #dd1144; +} + +.hidden { + display: none; +} + +/* Left side */ +#left { + overflow: auto; + width: 270px; + height: 100%; + position: fixed; +} + +/* Menu */ +#menu { + padding: 10px; +} + +#menu h3 { + border-bottom: 1px solid #cccccc; + margin-left: -10px; + margin-right: -10px; + padding: 0 10px; +} + +#menu ul { + list-style: none; + padding: 0; + margin: 0; +} + +#menu ul ul { + padding-left: 10px; +} + +#menu li { + white-space: nowrap; + position: relative; +} + +#menu a { + display: block; + padding: 3px; + border-radius: 3px; +} + +#menu a:hover { + background-color: #a4698e; + color: #ffffff !important; +} + +#menu .active > a { + font-weight: bold; + color: #000000; +} + +#menu .active > a.invalid { + color: #dd1144; +} + +#menu #groups span { + position: absolute; + top: 6px; + right: 3px; + cursor: pointer; + display: block; + width: 12px; + height: 12px; + background: url('collapsed.png') transparent 0 0 no-repeat; +} + +#menu #groups span:hover { + background-position: -12px 0; +} + +#menu #groups span.collapsed { + background-position: 0 -12px; +} + +#menu #groups span.collapsed:hover { + background-position: -12px -12px; +} + +#menu #groups ul.collapsed { + display: none; +} + +/* Search */ +#menu .form-search { + margin-top: 0; +} + +#menu .search-query { + height: auto; + width: 100%; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +/* Autocomplete */ +.ac_results { + border-radius: 4px; + margin-top: 2px; + background-clip: padding-box; + background-color: #ffffff; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 1px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + overflow: hidden; + z-index: 99999; +} + +.ac_results ul { + width: 100%; + list-style-position: outside; + list-style: none; + padding: 0; + margin: 0; +} + +.ac_results li { + margin: 0; + padding: 2px 5px; + cursor: default; + display: block; + overflow: hidden; + white-space: nowrap; +} + +.ac_results li strong { + color: #000000; +} + +.ac_over { + background-color: #a4698e; + color: #ffffff; +} + +.ac_results li.ac_over strong { + color: #ffffff; +} + + +/* Right side */ +#right { + overflow: auto; + margin-left: 275px; + height: 100%; + position: fixed; + left: 0; + right: 0; +} + +#rightInner { + max-width: 1000px; + min-width: 350px; +} + +/* Navigation */ +.navbar-fixed-top .container { + width: auto; + padding: 0 1em; +} + +.navbar .nav > li > span { + display: block; + color: #999999; + line-height: 19px; + padding: 10px 10px 11px; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} + +.navbar .nav > li.active > span { + background-color: #222222; + color: #ffffff; +} + +/* Content */ +#content { + clear: both; + padding: 5px 15px; +} + +#content > .description { + margin: 1.2em 0; +} + +#content .alert-info { + margin-top: 18px; +} + +dl.tree { + margin: 1.2em 0; + padding: 10px; +} + +dl.tree dd { + margin: 0; + padding: 0; + line-height: 18px; +} + +.elementList { + line-height: 24px; +} + +h2.switchable { + background: transparent url('sort.png') no-repeat center right; + cursor: pointer; +} + +.summary td:first-child { + text-align: right; +} + +#packages.summary td:first-child, #namespaces.summary td:first-child, .inherited.summary td:first-child, .used.summary td:first-child { + text-align: left; +} + +.summary tr:hover td { + background: #f6f6f4; +} + +.summary .description p { + margin: 0; +} + +.class #methods.summary .description p:first-child, .summary .description.detailed h4:first-child { + margin-top: 8px; +} + +.summary .description p + p, .summary .description ul, .summary .description pre, .summary .description.detailed h4 { + margin-top: 4px; +} + +.summary dl { + margin: 0; +} + +.summary dd { + margin: 0 0 0 25px; +} + +.summary dt, dd { + line-height: 24px; +} + +.name, .attributes { + white-space: nowrap; +} + +.value { + white-space: pre-wrap; +} + +td.name, td.attributes { + width: 1%; +} + +.class #methods .name { + width: auto; + white-space: normal; +} + +.class #methods .name > div > code { + white-space: pre-wrap; +} + +.class #methods .name > div > code span, .function .value > code { + white-space: nowrap; +} + +.class #methods td.name > div, .class td.description > div { + position: relative; + padding-right: 1em; +} + +.attributes code, .name code, dd code { + color: #468847; +} + +.anchor { + position: absolute; + top: 0; + right: 0; + line-height: 1; + font-size: 85%; + margin: 0; + color: #a4698e !important; +} + +.list { + margin: 0 0 5px 25px; + line-height: 24px; +} + +/* Splitter */ +#splitter { + position: fixed; + height: 100%; + width: 5px; + left: 270px; + background: #a4698e url('resize.png') left center no-repeat; + cursor: e-resize; +} + +#splitter.active { + opacity: .5; +} + +/* Footer */ +#footer { + border-top: 1px solid #e5e5e5; + clear: both; + color: #808080; + text-align: right; + padding: 2em 1em; + margin: 3em 0 40px 0; +} + +/* Tree */ +div.tree ul { + list-style: none; + background: url('tree-vertical.png') left repeat-y; + padding: 0; + margin-left: 20px; +} + +div.tree li { + margin: 0; + padding: 0; +} + +div.tree div { + padding-left: 30px; +} + +div.tree div.notlast { + background: url('tree-hasnext.png') left 10px no-repeat; +} + +div.tree div.last { + background: url('tree-last.png') left -240px no-repeat; +} + +div.tree li.last { + background: url('tree-cleaner.png') left center repeat-y; +} + +div.tree span.padding { + padding-left: 15px; +} + +/* Source code */ +#source { + margin: 1em 0 1em 1em; + padding: 0; +} + +.php-keyword1 { + color: #468847; + font-weight: bold; +} + +.php-keyword2 { + font-weight: bold; +} + +.php-var { + color: #c09853; + font-weight: bold; +} + +.php-num { + color: #006dcc; +} + +.php-quote { + color: #006dcc; +} + +.php-comment { + color: #929292; +} + +.xlang { + color: #468847; + font-weight: bold; +} + +.l { + background: #fbfbfc; + margin-right: 8px; + padding: 2px 2px 2px 8px; + color: #c0c0c0; +} + +.l:target { + background: #a4698e url('code-line.png') right center no-repeat; + color: #ffffff; +} + +/* Small screens */ +#rightInner.medium .name, #rightInner.medium .attributes { + white-space: normal; +} diff --git a/apigen/templates/woodocs/resources/tree-cleaner.png b/apigen/templates/woodocs/resources/tree-cleaner.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb9085be54558b89d461883c440f5f46f9736c1 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W@!3-o{`qYU7DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MQi~+A+G=b{|7Qd4_&SUQhc5+jv*Ddk`fLuF);BkFo-fR Uc0H0%1|6H_V+Po~-c6|n{Qgt!9f|Ns9ly*d9bkR{;h;uunKD=Fa!3l9f_Kmr5f WItCFtg_<`&IR;NxKbLh*2~7Z6!6X&{ literal 0 HcmV?d00001 diff --git a/apigen/templates/woodocs/resources/tree-last.png b/apigen/templates/woodocs/resources/tree-last.png new file mode 100644 index 0000000000000000000000000000000000000000..7f319f8f950bf32e5932d6d37e424806edb248a4 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^atsWNUpSb7ELpeQ13*f$#5JNMI6tkVJh3R1!7(L2 zDOJHUH!(dmC^a#qvhZZ84NwtVfKQ0)|NsAi%+N!Z>wuJvr;B4q#jQ6dH}Wzta4;|U z_5X4opPHS*8kOj$OSWE%%3h01{0i>}>H&cdp4bP0l+XkK DMdmrX literal 0 HcmV?d00001 diff --git a/apigen/templates/woodocs/resources/tree-vertical.png b/apigen/templates/woodocs/resources/tree-vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..384908b28e9958c9842f258350799221e3fa2020 GIT binary patch literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^B0$W@!3-o{`qYU7DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MQi~+A+G=b{|7Qd4_&SUQv9ASjv*Ddk`fNE^e`~8F))ZR VFl8!!&jCs?c)I$ztaD0e0sxs!A(8+9 literal 0 HcmV?d00001 diff --git a/apigen/templates/woodocs/robots.txt.latte b/apigen/templates/woodocs/robots.txt.latte new file mode 100644 index 00000000000..f895cd3a9f3 --- /dev/null +++ b/apigen/templates/woodocs/robots.txt.latte @@ -0,0 +1,14 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +User-agent: * +Disallow: +Sitemap: {$config->baseUrl}/sitemap.xml diff --git a/apigen/templates/woodocs/sitemap.xml.latte b/apigen/templates/woodocs/sitemap.xml.latte new file mode 100644 index 00000000000..7a9027626ea --- /dev/null +++ b/apigen/templates/woodocs/sitemap.xml.latte @@ -0,0 +1,36 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} + + + + {$config->baseUrl}/index.html + + + {$config->baseUrl}/{$namespace|namespaceUrl} + + + {$config->baseUrl}/{$package|packageUrl} + + +{define #elements} + + {$config->baseUrl}/{$element|elementUrl} + +{/define} + +{include #elements, elements => $classes} +{include #elements, elements => $interfaces} +{include #elements, elements => $traits} +{include #elements, elements => $exceptions} +{include #elements, elements => $constants} +{include #elements, elements => $functions} + diff --git a/apigen/templates/woodocs/source.latte b/apigen/templates/woodocs/source.latte new file mode 100644 index 00000000000..c803e3c48d1 --- /dev/null +++ b/apigen/templates/woodocs/source.latte @@ -0,0 +1,19 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $robots = false} + +{block #title}File {$fileName}{/block} + +{block #content} +
    {!$source|sourceAnchors|replaceRE:'~(\s*)(\d+):(\s*)([^\\n]*\\n)?~','$1$2$3$4'}
    +{/block} \ No newline at end of file diff --git a/apigen/templates/woodocs/todo.latte b/apigen/templates/woodocs/todo.latte new file mode 100644 index 00000000000..96ac9b1bbf2 --- /dev/null +++ b/apigen/templates/woodocs/todo.latte @@ -0,0 +1,129 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'todo'} + +{block #title}Todo{/block} + +{block #content} +
    +

    {include #title}

    + + {define #classes} + {foreach $items as $class} +
    {$class->name}{!$description|annotation:'todo':$class}
    + {include #classes, items => $todoClasses} +
    + {/if} + + {if $todoInterfaces} +

    Interfaces summary

    + + {include #classes, items => $todoInterfaces} +
    + {/if} + + {if $todoTraits} +

    Traits summary

    + + {include #classes, items => $todoTraits} +
    + {/if} + + {if $todoExceptions} +

    Exceptions summary

    + + {include #classes, items => $todoExceptions} +
    + {/if} + + {if $todoMethods} +

    Methods summary

    + + {foreach $todoMethods as $method} + + {var $count = count($method->annotations['todo'])} + + + {foreach $method->annotations['todo'] as $description} + {sep}{/sep} + {/foreach} + + {/foreach} +
    {$method->declaringClassName}{$method->name}(){!$description|annotation:'todo':$method}
    + {/if} + + {if $todoConstants} +

    Constants summary

    + + {foreach $todoConstants as $constant} + + {var $count = count($constant->annotations['todo'])} + {if $constant->declaringClassName} + + + {else} + + + {/if} + {foreach $constant->annotations['todo'] as $description} + {sep}{/sep} + {/foreach} + + {/foreach} +
    {$constant->declaringClassName}{$constant->name}{$constant->namespaceName}{$constant->shortName}{!$description|annotation:'todo':$constant}
    + {/if} + + {if $todoProperties} +

    Properties summary

    + + {foreach $todoProperties as $property} + + {var $count = count($property->annotations['todo'])} + + + {foreach $property->annotations['todo'] as $description} + {sep}{/sep} + {/foreach} + + {/foreach} +
    {$property->declaringClassName}${$property->name}{!$description|annotation:'todo':$property}
    + {/if} + + {if $todoFunctions} +

    Functions summary

    + + {foreach $todoFunctions as $function} + + {var $count = count($function->annotations['todo'])} + + + {foreach $function->annotations['todo'] as $description} + {sep}{/sep} + {/foreach} + + {/foreach} +
    {$function->namespaceName}{$function->shortName}{!$description|annotation:'todo':$function}
    + {/if} +
+{/block} \ No newline at end of file diff --git a/apigen/templates/woodocs/tree.latte b/apigen/templates/woodocs/tree.latte new file mode 100644 index 00000000000..adbfda8c2ad --- /dev/null +++ b/apigen/templates/woodocs/tree.latte @@ -0,0 +1,73 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'tree'} + +{block #title}Tree{/block} + +{define #tree} +
+
    + {var $level = -1} + {? foreach ($tree as $reflectionName => $reflection): } + {if $level === $tree->getDepth()} + + {elseif $level > $tree->getDepth()} + {!'
'|repeat:$level - $tree->getDepth()} + {elseif -1 !== $level} +
    + {/if} + +
  • {$reflectionName} + {var $interfaces = $reflection->ownInterfaces} + {if $interfaces} implements {foreach $interfaces as $interface} + {$interface->name}{sep}, {/sep} + {/foreach}{/if} + {var $traits = $reflection->ownTraits} + {if $traits}{if $interfaces}
    {/if} uses {foreach $traits as $trait} + {$trait->name}{sep}, {/sep} + {/foreach}{/if} +
    + + {var $level = $tree->getDepth()} + {? endforeach; } +
  • + {!'
'|repeat:$level} + +
+{/define} + +{block #content} +
+

{include #title}

+ + {if $classTree->valid()} +

Classes

+ {include #tree, tree => $classTree} + {/if} + + {if $interfaceTree->valid()} +

Interfaces

+ {include #tree, tree => $interfaceTree} + {/if} + + {if $traitTree->valid()} +

Traits

+ {include #tree, tree => $traitTree} + {/if} + + {if $exceptionTree->valid()} +

Exceptions

+ {include #tree, tree => $exceptionTree} + {/if} +
+{/block}